1.2. Setup
Below we load required libraries, mapTheme and plotTheme for
consistent styling, and specify a palette of colors for
visualizations
library(tigris)
library(tidyverse)
library(sf)
library(raster)
library(knitr)
library(kableExtra)
library(tidycensus)
library(tigris)
library(FNN)
#library(QuantPsyc) # JE Note: in R 4.1, QuantPsyc package not available.
library(caret)
library(yardstick)
library(pscl)
library(plotROC)
library(ggrepel)
library(pROC)
library(grid)
library(gridExtra)
library(viridis)
library(igraph)
library(mapview)
library(exactextractr)
plotTheme <- theme(
plot.title =element_text(size=12),
plot.subtitle = element_text(size=8),
plot.caption = element_text(size = 6),
axis.text.x = element_text(size = 10, angle = 45, hjust = 1),
axis.text.y = element_text(size = 10),
axis.title.y = element_text(size = 10),
# Set the entire chart region to blank
panel.background=element_blank(),
plot.background=element_blank(),
#panel.border=element_rect(colour="#F0F0F0"),
# Format the grid
panel.grid.major=element_line(colour="#D0D0D0",size=.75),
axis.ticks=element_blank())
mapTheme <- theme(plot.title =element_text(size=12),
plot.subtitle = element_text(size=8),
plot.caption = element_text(size = 6),
axis.line=element_blank(),
axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks=element_blank(),
axis.title.x=element_blank(),
axis.title.y=element_blank(),
panel.background=element_blank(),
panel.border=element_blank(),
panel.grid.major=element_line(colour = 'transparent'),
panel.grid.minor=element_blank(),
legend.direction = "vertical",
legend.position = "right",
plot.margin = margin(1, 1, 1, 1, 'cm'),
legend.key.height = unit(1, "cm"), legend.key.width = unit(0.2, "cm"))
palette1 <- c("#cd0000","#0059c7")
palette2 <- c("#41b6c4","#253494")
palette4 <- c("#a1dab4","#41b6c4","#2c7fb8","#253494")
palette5 <- c("#ffffcc","#a1dab4","#41b6c4","#2c7fb8","#253494")
palette10 <- c("#f7fcf0","#e0f3db","#ccebc5","#a8ddb5","#7bccc4",
"#4eb3d3","#2b8cbe","#0868ac","#084081","#f7fcf0")
We also include several helper functions.
quintilesBreaks takes a dataframe and a column and outputs
the quintiles breaks, helping shorten the below ggplot
calls.
It takes longer to ggplot a polygon fishnet with
geom_sf than it does to plot geom_point. To
cut down on plotting time, the xyC (for ‘XY Coordinates’)
takes a fishnet sf and converts it to a dataframe of grid
cell centroid coordinates.
rast is a function allowing us to quickly plot raster
values in ggplot.
#this function converts a column in to quintiles. It is used for mapping.
quintileBreaks <- function(df,variable) {
as.character(quantile(df[[variable]],
c(.01,.2,.4,.6,.8),na.rm=T))
}
#This function can be used to convert a polygon sf to centroids xy coords.
xyC <- function(aPolygonSF) {
as.data.frame(
cbind(x=st_coordinates(st_centroid(aPolygonSF))[,1],
y=st_coordinates(st_centroid(aPolygonSF))[,2]))
}
#this function convert a raster to a data frame so it can be plotted in ggplot
rast <- function(inRaster) {
data.frame(
xyFromCell(inRaster, 1:ncell(inRaster)),
value = getValues(inRaster)) }
2. Data Wrangling & Feature Engineering
In this section a considerable amount of vector and raster data is
wrangled together into a regression-ready dataset. The following
datasets are used:
2.2 - 2.3: Land cover data downloaded
from the Multi-Resolution Land Characteristics Consortium’s National
Land Cover Database (NLCD) includes annual land cover and land cover
change raster data for the Charlottesville and Albemarle countries.
These data are sampled to a 4,000 by 4,000 ft^2 fishnet.
2.4: Population data is downloaded from the U.S. Census and joined to
the fishnet by distributing Block population totals proportionally to
each grid cell.
2.5: Highway vectors are downloaded from Virginia State open data
website and used to wrangle highway proximity features.
2.6: The land cover change data is used to engineer spatial lag
features.
2.7: County polygons are downloaded using the tigris
package.
2.8: Each feature is wrangled into a final dataset.
Other raster features are created such as distance to highways, for
instance. These rasters are then integrated with a vector fishnet.
Additional feature engineering is performed on the vector-side providing
a simple, but comprehensive snapshot of the development process in and
around Charlottesville MSA between 2001 and 2019.
2.2. Land Cover Change Data
We aim to forecast the dependent variable of land cover change
between 2001 and 2019. In this section, we load the land cover raster
data and reclassify it, integrating it with a vector fishnet. This
allows us to parameterize spatial relationships in a regression
context.
The table below shows descriptions of each categorical land cover
type in the land cover data. Below, we will reclassify these data into
more useful categories.
| Water |
|
| 11 |
Open Water - areas of open water, generally with less than 25% cover
of vegetation or soil. |
| 12 |
Perennial Ice/Snow - areas characterized by a perennial cover of ice
and/or snow, generally greater than 25% of total cover. |
| Developed |
|
| 21 |
Developed, Open Space - areas with a mixture of some constructed
materials, but mostly vegetation in the form of lawn grasses. Impervious
surfaces account for less than 20% of total cover. These areas most
commonly include large-lot single-family housing units, parks, golf
courses, and vegetation planted in developed settings for recreation,
erosion control, or aesthetic purposes. |
| 22 |
Developed, Low Intensity - areas with a mixture of constructed
materials and vegetation. Impervious surfaces account for 20% to 49%
percent of total cover. These areas most commonly include single-family
housing units. |
| 23 |
Developed, Medium Intensity - areas with a mixture of constructed
materials and vegetation. Impervious surfaces account for 50% to 79% of
the total cover. These areas most commonly include single-family housing
units. |
| 24 |
Developed High Intensity - highly developed areas where people
reside or work in high numbers. Examples include apartment complexes,
row houses and commercial/industrial. Impervious surfaces account for
80% to 100% of the total cover. |
| Barren |
|
| 31 |
Barren Land (Rock/Sand/Clay) - areas of bedrock, desert pavement,
scarps, talus, slides, volcanic material, glacial debris, sand dunes,
strip mines, gravel pits and other accumulations of earthen material.
Generally, vegetation accounts for less than 15% of total cover. |
| Forest |
|
| 41 |
Deciduous Forest - areas dominated by trees generally greater than 5
meters tall, and greater than 20% of total vegetation cover. More than
75% of the tree species shed foliage simultaneously in response to
seasonal change. |
| 42 |
Evergreen Forest - areas dominated by trees generally greater than 5
meters tall, and greater than 20% of total vegetation cover. More than
75% of the tree species maintain their leaves all year. Canopy is never
without green foliage. |
| 43 |
Mixed Forest - areas dominated by trees generally greater than 5
meters tall, and greater than 20% of total vegetation cover. Neither
deciduous nor evergreen species are greater than 75% of total tree
cover. |
| Shrubland |
|
| 51 |
Dwarf Scrub - Alaska only areas dominated by shrubs less than 20
centimeters tall with shrub canopy typically greater than 20% of total
vegetation. This type is often co-associated with grasses, sedges,
herbs, and non-vascular vegetation. |
| 52 |
Shrub/Scrub - areas dominated by shrubs; less than 5 meters tall
with shrub canopy typically greater than 20% of total vegetation. This
class includes true shrubs, young trees in an early successional stage
or trees stunted from environmental conditions. |
| Herbaceous |
|
| 71 |
Grassland/Herbaceous - areas dominated by gramanoid or herbaceous
vegetation, generally greater than 80% of total vegetation. These areas
are not subject to intensive management such as tilling, but can be
utilized for grazing. |
| 72 |
Sedge/Herbaceous - Alaska only areas dominated by sedges and forbs,
generally greater than 80% of total vegetation. This type can occur with
significant other grasses or other grass-like plants, and includes sedge
tundra, and sedge tussock tundra. |
| 73 |
Lichens - Alaska only areas dominated by fruticose or foliose
lichens generally greater than 80% of total vegetation. |
| 74 |
Moss - Alaska only areas dominated by mosses, generally greater than
80% of total vegetation. |
| Planted/Cultivated |
|
| 81 |
Pasture/Hay - areas of grasses, legumes, or grass-legume mixtures
planted for livestock grazing or the production of seed or hay crops,
typically on a perennial cycle. Pasture/hay vegetation accounts for
greater than 20% of total vegetation. |
| 82 |
Cultivated Crops - areas used for the production of annual crops,
such as corn, soybeans, vegetables, tobacco, and cotton, and also
perennial woody crops such as orchards and vineyards. Crop vegetation
accounts for greater than 20% of total vegetation. This class also
includes all land being actively tilled. |
| Wetlands |
|
| 90 |
Woody Wetlands - areas where forest or shrubland vegetation accounts
for greater than 20% of vegetative cover and the soil or substrate is
periodically saturated with or covered with water. |
| 95 |
Emergent Herbaceous Wetlands - Areas where perennial herbaceous
vegetation accounts for greater than 80% of vegetative cover and the
soil or substrate is periodically saturated with or covered with
water. |
Several raster layers have been provided for this analysis:
We read in CvilleMSA - this is the extent of the
study area
lc_change is a raster of land cover change - where
there were conversions between one land cover and another on the time
frame 2001-2019. We plot the raster using ggplot and the
rast function specified above.
Note that lc_change is projected as
NAD 1983 StatePlane Virginia South FIUS and is spatially
referred using EPSG:2284. The original land cover raster is
at a 30 meter by 30 meter resolution. The rasters provided are
ultimately resampled up to 4000 feet by 4000 feet.
CvilleMSA <-
# High-Resolution Version:
st_read("https://raw.githubusercontent.com/Yingtong-Z/sprawl-forecasting/b528d725eaea5dd778c41fa644b5e6f71d6291b2/data/Cville_Hex.geojson") %>%
# Medium-Resolution Version:
# st_read("https://raw.githubusercontent.com/Yingtong-Z/sprawl-forecasting/a3649c5ff06585500c219b3990991065ad3c0a6d/data/Cville_Tesselated_Coarse.geojson") %>%
# Low-Resolution Version:
# st_read("https://raw.githubusercontent.com/Yingtong-Z/sprawl-forecasting/a3649c5ff06585500c219b3990991065ad3c0a6d/data/Cville_Tesselated_Coarser.geojson") %>%
st_transform('EPSG:2284')
lc_change = raster("https://raw.githubusercontent.com/Yingtong-Z/sprawl-forecasting/b528d725eaea5dd778c41fa644b5e6f71d6291b2/data/LC_Change_2001_2019.tif")
lc_change <- projectRaster(lc_change, crs = CRS("+init=epsg:2284"))
We now plot Land Cover Change within our MSA.
ggplot() +
geom_raster(data=rast(lc_change) %>% na.omit %>% filter(value > 0),
aes(x,y,fill=as.factor(value))) +
scale_fill_viridis(direction = -1, discrete=TRUE, name ="Land Cover\nChange") +
labs(title = "Land Cover Change, 2000-2010") +
mapTheme +
theme(legend.direction="horizontal")

Next, we reclassify the raster such that all the developed grid cell
values receive a value of 1 and all other values receive a value of 0.
This is done using a reclassify matrix. The matrix reads row by row. Row
1 says any grid cell ranging from 0 to 12 takes a value of 0; 13 or
greater through 24, a value of 1; and all other values take 0.
reclassMatrix <- matrix(c(
0,2,0,
2,3,1,
3,Inf,0),
ncol=3, byrow=T)
reclassMatrix
## [,1] [,2] [,3]
## [1,] 0 2 0
## [2,] 2 3 1
## [3,] 3 Inf 0
Now reclassify and convert all 0’s to NA.
We apply a name to the raster with names. This is done to
make it faster to join raster to the fishnet below.
lc_change2 <-
reclassify(lc_change,reclassMatrix)
lc_change2[lc_change2 < 1] <- NA
names(lc_change2) <- "lc_change"
ggplot() +
geom_sf(data=CvilleMSA, fill = "grey", color = "transparent")+
geom_raster(data=rast(lc_change2) %>% na.omit,
aes(x,y,fill=as.factor(value))) +
scale_fill_viridis(discrete=TRUE, name ="Land Cover\nChange") +
labs(title="Development Land Use Change") +
mapTheme

The vector fishnet is then plotted.
ggplot() +
geom_sf(data=CvilleMSA, fill = "grey", color= "black") +
labs(title="Hexagonal Fishnet, 250k sq ft cells") +
mapTheme

The code below converts the raster to an sf point layer
and then joins the points to the fishnet with aggregate.
Finally, the fishnet variable lc_change is created that is
1 where new development has occurred and 0
where it has not. This is our dependent variable and encoded as a
factor.
changePoints <-
rasterToPoints(lc_change2) %>%
as.data.frame() %>%
st_as_sf(coords = c("x", "y"), crs = st_crs(CvilleMSA))
fishnet <-
aggregate(changePoints, CvilleMSA, sum) %>%
mutate(lc_change = ifelse(is.na(lc_change),0,1),
lc_change = as.factor(lc_change))
# Plot
# To speed up the mapping process, fishnet polygons are converted to centroid points using the `xyC` function defined earlier
ggplot() +
geom_sf(data=fishnet, aes(fill=lc_change), color=NA) +
scale_fill_manual(values = palette2, labels=c("No Change","New Development"), name = "") +
labs(title = "Land Cover Development Change", subtitle = "As fishnet centroids") +
mapTheme

2.3. Land Cover in 2001
It is reasonable to hypothesize that the propensity of new
development is a function of existing land cover categories. In this
section we identify these other land cover categories from 2001 and
integrate each with the fishnet.
lc_2001 <- raster("https://raw.githubusercontent.com/Yingtong-Z/sprawl-forecasting/b528d725eaea5dd778c41fa644b5e6f71d6291b2/data/Cville_LC_2001.tif")
ggplot() +
geom_raster(data=rast(lc_2001) %>% na.omit %>% filter(value > 0),
aes(x,y,fill=as.factor(value))) +
scale_fill_viridis(discrete=TRUE, name ="") +
labs(title = "Land Cover, 2001") +
mapTheme +
theme(legend.direction="horizontal")

The table below shows the approach taken to recoded existing land
cover codes into the categories used in our analysis. In the code block
below new rasters are generated and names are applied.
Naming ensures that when the raster is integrated with the fishnet, the
column reflects the appropriate raster.
| Open Space as well as Low, Medium and High Intensity
Development |
Developed |
| Deciduous, Evergreen, and Mixed Forest |
Forest |
| Pasture/Hay and Cultivated Crops |
Farm |
| Woody and Emergent Herbaceous Wetlands |
Woodlands |
| Barren Land, Dwarf Scrub, and Grassland/Herbaceous |
Other Undeveloped |
| Water |
Water |
developed <- lc_2001 == 21 | lc_2001 == 22 | lc_2001 == 23 | lc_2001 == 24
forest <- lc_2001 == 41 | lc_2001 == 42 | lc_2001 == 43
farm <- lc_2001 == 81 | lc_2001 == 82
wetlands <- lc_2001 == 90 | lc_2001 == 95
otherUndeveloped <- lc_2001 == 52 | lc_2001 == 71 | lc_2001 == 31
water <- lc_2001 == 11
names(developed) <- "developed"
names(forest) <- "forest"
names(farm) <- "farm"
names(wetlands) <- "wetlands"
names(otherUndeveloped) <- "otherUndeveloped"
names(water) <- "water"
Next, each raster is aggregated to the fishnet by way of a function
called aggregateRaster. Here, the process used above to To
do this, a function is created below that loops through a list of
rasters, converts the ith raster to points, filters only points
that have value of 1 (ie. is the ith land cover
type), and then aggregates to the fishnet.
Here is the function.
aggregateRaster <- function(inputRasterList, theFishnet) {
#create an empty fishnet with the same dimensions as the input fishnet
theseFishnets <- theFishnet %>% dplyr::select()
#for each raster in the raster list
for (i in inputRasterList) {
#create a variable name corresponding to the ith raster
varName <- names(i)
#convert raster to points as an sf
thesePoints <-
rasterToPoints(i) %>%
as.data.frame() %>%
st_as_sf(coords = c("x", "y"), crs = st_crs(theFishnet)) %>%
filter(.[[1]] == 1)
#aggregate to the fishnet
thisFishnet <-
aggregate(thesePoints, theFishnet, length) %>%
mutate(!!varName := ifelse(is.na(.[[1]]),0,1))
#add to the larger fishnet
theseFishnets <- cbind(theseFishnets,thisFishnet)
}
#output all aggregates as one large fishnet
return(theseFishnets)
}
The theRasterList of land cover types in 2001 is created
and then fed into aggregateRaster. The result is converted
to long form grid cell centroids and plot as small multiple maps.
Note the inclusion of st_cast here which convert all
geometries to POLYGON. If we create a frequency table of
geometry types in aggregatedRasters, we will notice some
and handful of MULTIPOLYGONS. Try
table(st_geometry_type(aggregatedRasters)). These rogue
multipolygons break the xyC function which is designed to
find grid cell centroids. After all, there is no one centroid of several
combined polygons. Thus st_cast ensures all geometries are
just POLYGON.
theRasterList <- c(developed,forest,farm,wetlands,otherUndeveloped,water)
aggregatedRasters <-
aggregateRaster(theRasterList, CvilleMSA) %>%
dplyr::select(developed,forest,farm,wetlands,otherUndeveloped,water) %>%
mutate_if(is.numeric,as.factor)
# reassign cells with more than one lc value to just one based on hierarchy shown here
aggregatedRasters <- aggregatedRasters %>%
mutate(farm = ifelse(developed == 0 & farm == 1, 1, 0)) %>%
mutate(forest = ifelse(developed == 0 & farm == 0 & forest == 1, 1, 0)) %>%
mutate(wetlands = ifelse(developed == 0 & farm == 0 & forest == 0 & wetlands == 1, 1, 0)) %>%
mutate(water = ifelse(developed == 0 & farm == 0 & forest == 0 & wetlands == 0 & water == 1, 1, 0)) %>%
mutate(otherUndeveloped = ifelse(developed == 0 & farm == 0 & forest == 0 & wetlands == 0 & water == 0 & otherUndeveloped == 1, 1, 0))
aggregatedRasters %>%
gather(var,value,developed:water) %>%
st_cast("POLYGON") %>% #just to make sure no weird geometries slipped in
mutate(X = xyC(.)$x,
Y = xyC(.)$y) %>%
ggplot() +
geom_sf(data=CvilleMSA) +
geom_point(aes(X,Y, colour=as.factor(value)), size =0.1) +
facet_wrap(~var) +
scale_colour_manual(values = palette2,
labels=c("Other","Land Cover"),
name = "") +
labs(title = "Land Cover Types, 2001",
subtitle = "As fishnet centroids") +
mapTheme

2.4. Census Data
Population and population change are crucial demand-side factors for
predicting Development_Demand. We obtain 2000 and 2020
census data through the tidycensus package to represent the
demographic features for 2001 and 2019. These data are downloaded at a
block group geography and thus, an approach is needed to reconcile
tracts and fishnet geometries. This is accomplished using a technique
called areal weighted interpolation.
Below we get the demographic data including
total_population, total_housing_units,
total_vacant_households,
total_white,total_graduate_degree_holders for
both 2000 and 2020.
#function to get and clean 2000 census data (decennial)
clean_2000_census_data <- function() {
#varibles to use for 2000 decenital variables
variables2000A <- c("P001001", "H001001", "H003003", "P003003")
names(variables2000A) <- c("total_population", "total_housing_units", "total_vacant_households", "total_white")
variables2000B <- c("P053001","P036021","P036044")
names(variables2000B) <- c("median_household_income","Male Total graduate degree holders", "Female Total graduate degree holders")
#function to get 2000 decennial variables
get_decenial_variables <- function(year, sumfile, variables, county) {
data <- get_decennial(
geography = "block group",
year = year,
sumfile = sumfile,
variables = variables,
state = "VA",
county = county,
output = "wide",
geometry = TRUE
)
return(data)
}
# get variables from different decennial files for CV and AM
data_cv1 <- get_decenial_variables(2000,'sf1', variables2000A, "Charlottesville")
data_cv2 <- get_decenial_variables(2000,'sf3', variables2000B, "Charlottesville")
data_am1 <- get_decenial_variables(2000,'sf1', variables2000A, "Albemarle")
data_am2 <- get_decenial_variables(2000,'sf3', variables2000B, "Albemarle")
# join the data
cvam_census1 <- rbind(data_cv1, data_am1)
cvam_census2 <- rbind(data_cv2, data_am2)
cvam_census <- st_join(cvam_census1,
cvam_census2[, c("median_household_income", "Male Total graduate degree holders", "Female Total graduate degree holders")],
join = st_equals) %>%
mutate(`total_graduate_degree_holders` = `Male Total graduate degree holders` + `Female Total graduate degree holders`) %>%
dplyr::select(-`Male Total graduate degree holders`, -`Female Total graduate degree holders`)
return(cvam_census)
}
#function to get and clean 2020 census data (acs5)
clean_2020_census_data <- function() {
#varibles to use for 2020 decenital variables
variables2020A <- c("B01003_001E", "B25001_001E", "B25002_003E", "B02001_002E")
names(variables2020A) <- c("total_population", "total_housing_units", "total_vacant_households", "total_white")
variables2020B <- c("B19049_001E","B14002_022E","B14002_046E")
names(variables2020B) <- c("median_household_income","Male Total graduate degree holders", "Female Total graduate degree holders")
#function to get 2020 decenital variables
get_acs_variables <- function(year, variables, county) {
data <- get_acs(
geography = "block group",
survey = "acs5",
year = year,
variables = variables,
state = "VA",
county = county,
output = "wide",
geometry = TRUE
)
return(data)
}
# get variables from different acs files for CV and AM
data_cv1 <- get_acs_variables(2020, variables2020A, "Charlottesville") %>% dplyr::select(!ends_with("M"))
data_cv2 <- get_acs_variables(2020, variables2020B, "Charlottesville") %>% dplyr::select(!ends_with("M"))
data_am1 <- get_acs_variables(2020, variables2020A, "Albemarle") %>% dplyr::select(!ends_with("M"))
data_am2 <- get_acs_variables(2020, variables2020B, "Albemarle") %>% dplyr::select(!ends_with("M"))
# join the data
cvam_census1 <- rbind(data_cv1, data_am1)
cvam_census2 <- rbind(data_cv2, data_am2)
cvam_census <- st_join(cvam_census1,
cvam_census2[, c("median_household_income", "Male Total graduate degree holders", "Female Total graduate degree holders")],
join = st_equals) %>%
mutate(`total_graduate_degree_holders` = `Male Total graduate degree holders` + `Female Total graduate degree holders`) %>%
dplyr::select(-`Male Total graduate degree holders`, -`Female Total graduate degree holders`)
return(cvam_census)
}
# run the functions
cvam_2000_census <- clean_2000_census_data() # 2000 census data
cvam_2020_census <- clean_2020_census_data() # 2020 census data
Additional Census Data Formatting for 2020.
cvam_2020_census <- cvam_2020_census %>%
mutate(total_population2020 = total_population) %>%
mutate(total_housing_units2020 = total_housing_units) %>%
mutate(total_vacant_households2020 = total_vacant_households) %>%
mutate(total_white2020 = total_white) %>%
mutate(median_household_income2020 = median_household_income) %>%
mutate(total_graduate_degree_holders2020 = total_graduate_degree_holders) %>%
dplyr::select(-`total_population`, -`total_housing_units`, -`total_vacant_households`, -`total_white`, -`median_household_income`, -`total_graduate_degree_holders`)
Feature of 2000 total population data is plotted.
ggplot()+
geom_sf(data = cvam_2000_census,
aes(fill = total_population)) + mapTheme

To join the demographic data to fishnet, a spatial join would be
inappropriate as it would assign the same demographic feature value from
one tract to the many intersecting grid cells. Instead, the area
weighted interpolation function, st_interpolate_aw, assigns
a proportion of a tract’s demographic feature to a grid cell weighted by
the proportion of the tract that intersects the grid cell. This works
best of course, when we assume that the block group population is
uniformly distributed across the block group. This is typically not a
great assumption. However, it is a reasonable here particularly given
demographic features in a regression and not an outcome that needs to be
measured with significant precision.
CvilleMSA <-
CvilleMSA %>%
rownames_to_column("fishnetID") %>%
mutate(fishnetID = as.numeric(fishnetID)) %>%
dplyr::select(fishnetID)
# Transform Projection of Census Data
cvam_2000_census <- cvam_2000_census %>%
st_transform(st_crs(CvilleMSA))
cvam_2020_census <- cvam_2020_census %>%
st_transform(st_crs(CvilleMSA))
# Join interpolated 2000 census data with fishnet
# 2000 interpolation function
interpolate_column2000 <- function(column_name) {
interpolated_data <-
st_interpolate_aw(cvam_2000_census[column_name], CvilleMSA, extensive=TRUE) %>%
as.data.frame(.) %>%
rownames_to_column(var = "fishnetID") %>%
left_join(CvilleMSA %>%
mutate(fishnetID = as.character(fishnetID)),
., by=c("fishnetID"='fishnetID')) %>%
mutate(!!column_name := replace_na(!!sym(column_name), 0)) %>%
dplyr::select(column_name)
return(interpolated_data)
}
fishnetPopulation00 <- interpolate_column2000('total_population')
fishnetHHunit00 <- interpolate_column2000('total_housing_units')
fishnetVacHH00 <- interpolate_column2000('total_vacant_households')
fishnetWhiteP00 <- interpolate_column2000('total_white')
fishnetGraduate00 <- interpolate_column2000('total_graduate_degree_holders')
# 2020 interpolation function
interpolate_column2020 <- function(column_name) {
interpolated_data <-
st_interpolate_aw(cvam_2020_census[column_name], CvilleMSA, extensive=TRUE) %>%
as.data.frame(.) %>%
rownames_to_column(var = "fishnetID") %>%
left_join(CvilleMSA %>%
mutate(fishnetID = as.character(fishnetID)),
., by=c("fishnetID"='fishnetID')) %>%
mutate(!!column_name := replace_na(!!sym(column_name), 0)) %>%
dplyr::select(column_name)
return(interpolated_data)
}
fishnetPopulation20 <- interpolate_column2020('total_population2020')
fishnetHHunit20 <- interpolate_column2020('total_housing_units2020')
fishnetWhiteP20 <- interpolate_column2020('total_white2020')
fishnet_pop_change_00_20 <-
cbind(fishnetPopulation00,fishnetPopulation20) %>%
dplyr::select(total_population,total_population2020) %>%
mutate(pop_Change_00_20 = total_population2020 - total_population)
#plot data from 2000
ggplot()+
geom_sf(data = fishnetPopulation00,
aes(fill = total_population), color = "transparent")+ mapTheme

2.5. Highway Distance
Accessibility is a key determinant of development potential
particularly in a sprawling city like Charlottesville MSA. Accessibility
features are engineered by measuring distance from each grid cell to its
nearest highway.
First highway vectors are downloaded from the Virginia State open
data website in geojson format; projected and subset to the
subset using st_intersection. Below, new development is
mapped with the highway overlay.
CvilleHighways <-
#st_read("https://raw.githubusercontent.com/Yingtong-Z/sprawl-forecasting/b528d725eaea5dd778c41fa644b5e6f71d6291b2/data/Cville_Highways.geojson") %>%
st_read("https://raw.githubusercontent.com/Yingtong-Z/sprawl-forecasting/992db0a2e854c764df2bbafcaac6d424975d4bdc/data/Cville_Highways2.geojson") %>%
st_transform(st_crs(CvilleMSA)) %>%
st_intersection(CvilleMSA)
ggplot() +
geom_point(data=fishnet,
aes(x=xyC(fishnet)[,1], y=xyC(fishnet)[,2],colour=lc_change),size=0.1) +
geom_sf(data=CvilleHighways, size = 1) +
scale_colour_manual(values = palette2,
labels=c("No Change","New Development")) +
labs(title = "New Development and Highways",
subtitle = "As fishnet centroids") +
mapTheme

Below are some great r-based raster skills. The distance from each
grid cell to its nearest highway segment is measured.
First, the highway layer is converted to raster. This is done by
creating an emptyRaster of NA grid cells at
the same spatial extent as lc_change. Then,
highway_raster is created by converting
CvilleHighways to sp form and then to applying
rasterize. The raster is then converted to points with
rasterToPoints and st_as_sf, then
aggregate is used to calculate mean distance by grid
cell.
emptyRaster <- lc_change
emptyRaster[] <- NA
highway_raster <-
as(CvilleHighways,'Spatial') %>%
rasterize(.,emptyRaster)
highway_raster_distance <- distance(highway_raster)
names(highway_raster_distance) <- "distance_highways"
highwayPoints <-
rasterToPoints(highway_raster_distance) %>%
as.data.frame() %>%
st_as_sf(coords = c("x", "y"), crs = st_crs(CvilleMSA))
highwayPoints_fishnet <-
aggregate(highwayPoints, CvilleMSA, mean) %>%
mutate(distance_highways = ifelse(is.na(distance_highways),0,distance_highways))
ggplot() +
geom_sf(data=CvilleMSA) +
geom_point(data=highwayPoints_fishnet, aes(x=xyC(highwayPoints_fishnet)[,1],
y=xyC(highwayPoints_fishnet)[,2],
colour=factor(ntile(distance_highways,5))),size=1.5) +
scale_colour_manual(values = palette5,
labels=substr(quintileBreaks(highwayPoints_fishnet,"distance_highways"),1,8),
name="Quintile\nBreaks") +
geom_sf(data=CvilleHighways, colour = "red") +
labs(title = "Distance to Highways",
subtitle = "As fishnet centroids; Highways visualized in red") +
mapTheme

2.6. Add Slope Variable
SlopeRaster <- raster("https://raw.githubusercontent.com/Yingtong-Z/sprawl-forecasting/9f810ec8e1e9774c51d83194e22848e8240d26f7/data/Cville_Slope.tif")
# Check the CRS of the raster and the fishnet data
raster_crs <- crs(SlopeRaster)
fishnet_crs <- st_crs(fishnet)
# If CRS doesn't match, reproject the fishnet data to match the raster CRS
if (raster_crs != fishnet_crs) {
fishnet <- st_transform(fishnet, raster_crs)}
# Calculate the mean raster values within each fishnet cell
mean_values <- exact_extract(SlopeRaster, fishnet, fun = 'mean')
# Add the mean values as a new column to the fishnet data
fishnet$slope <- mean_values
# Transform the fishnet back EPSG:2284
fishnet <- st_transform(fishnet, 2284)
2.7. The Spatial Lag of Development
Our model hypothesizes that development demand partly depends on
existing development patterns. Accessibility plays a significant role in
traditional ‘bid-rent’ economic models of development. However, this
model assumes shared preferences for central city access, which may not
hold true in sprawling regions like Charlottesville MSA, where suburban
locations are desirable.
To forecast growth, features must be created to associate these
patterns with development. Accessibility is measured via spatial lag,
hypothesizing that new development depends on distance to existing
development. The average distance from each grid cell to its two nearest
developed neighboring grid cells in 2001 is calculated using the
nn_function.
# The function below calculates average nearest neighbor distance between k point layers. The first parameter specifies coordinates that we want to `measureFrom`, in this case, `fishnet` centroids. The second, indicates the point layer we wish to `measureTo`.
nn_function <- function(measureFrom,measureTo,k) {
#convert the sf layers to matrices
measureFrom_Matrix <-
as.matrix(measureFrom)
measureTo_Matrix <-
as.matrix(measureTo)
nn <-
get.knnx(measureTo, measureFrom, k)$nn.dist
output <-
as.data.frame(nn) %>%
rownames_to_column(var = "thisPoint") %>%
gather(points, point_distance, V1:ncol(.)) %>%
arrange(as.numeric(thisPoint)) %>%
group_by(thisPoint) %>%
summarize(pointDistance = mean(point_distance)) %>%
arrange(as.numeric(thisPoint)) %>%
dplyr::select(-thisPoint) %>%
pull()
return(output)
}
Next, the function appending the lag distance to
fishnet. There are 3 inputs. The fishnet which
is converted to a coordinate data frame with the xyC
function. 2001 developed areas are created using filter.
The map below illustrates relative accessibility from every grid cell to
nearby development.
fishnet$lagDevelopment <-
nn_function(xyC(fishnet),
xyC(filter(aggregatedRasters,developed==1)),
2)
ggplot() +
geom_sf(data=CvilleMSA) +
geom_point(data=fishnet,
aes(x=xyC(fishnet)[,1], y=xyC(fishnet)[,2],
colour=factor(ntile(lagDevelopment,5))), size=0.01) +
scale_colour_manual(values = palette5,
labels=substr(quintileBreaks(fishnet,"lagDevelopment"),1,7),
name="Quintile\nBreaks") +
labs(title = "Spatial Lag to 2001 Development",
subtitle = "As fishnet centroids") +
mapTheme

2.8. MSA Counties
The tigris package allows Virginia county geometries to
be downloaded. A spatial subset returns only the counties in the MSA.
Note that the subset includes a negative 1000ft st_buffer.
This is done because the spatial extent of CvilleMSA
intersects county boundaries that are actually outside of our study
area. Buffering CvilleMSA slightly limits the intersection
range to only those counties in the study area.
Once studyAreaCounties is created, it is
st_joined with dat such that each grid cells
knows which county it’s in.
options(tigris_class = "sf")
studyAreaCounties <-
counties("Virginia") %>%
st_transform(st_crs(CvilleMSA)) %>%
dplyr::select(NAME) %>%
.[st_buffer(CvilleMSA,-1), , op=st_intersects]
2.9. Create the Final Dataset
The last step is to bring together all the disparate feature layers
into a final dataset that can be used for analysis. The various fishnet
layers are cbind together, needed features are extracted
and the final fishnet, dat is then joined with
studyAreaCounties to assign each grid cell to a county.
developed10 is created to designate those areas that have
already been developed through 2019. Finally, any grid cell that has a
water land cover designation is removed.
dat <-
cbind(
fishnet, highwayPoints_fishnet,fishnetGraduate00,
fishnetHHunit00,fishnetPopulation00,fishnetVacHH00,
fishnetWhiteP00,fishnetPopulation20,fishnet_pop_change_00_20,aggregatedRasters,
fishnetHHunit20, fishnetWhiteP20) %>%
dplyr::select(lc_change, developed, forest, farm, wetlands, otherUndeveloped, slope,water,
total_population,total_graduate_degree_holders,total_housing_units,
total_vacant_households,total_white,total_population2020,pop_Change_00_20,distance_highways, lagDevelopment, total_housing_units2020, total_white2020) %>%
st_join(studyAreaCounties) %>%
mutate(NAME = ifelse(NAME == "Charlottesville", "Charlottesville", "Albemarle")) %>%
mutate(developed10 = ifelse(lc_change == 1 & developed == 1, 0, developed)) %>%
filter(water == 0)
studyAreaCounties <- studyAreaCounties %>%
filter(NAME == "Albemarle" | NAME == "Charlottesville")
ggplot() +
geom_sf(data=studyAreaCounties,
aes(fill = NAME)) +
labs(title = "Study Area Counties") +
mapTheme

3. Exploratory Analysis
In this section we explore the extent to which each features is
associated with development change. If the goal was to predict a
continuous variable, scatterplots and correlation coefficients make this
process straightforward and relatively easy to explain to a
non-technical decison maker.
In this case however, the dependent variable is a binary outcome -
either a grid cell was developed between 2001 and 2011 or it wasn’t. In
this case, the relevant question is whether for a given feature, there
is a statistically significant difference between areas that changed and
areas that did not. These differences are explored in a set of plots
below. For models with lots of features, these plots could be compliment
by a series of difference in means statistical tests.
The below code block selects the highways and spatial
lag features, converts each to long form and plots each as bar plots.
Note that geom_bar calculates the mean. The
mean distance_highways is significantly lower for the
New Development category compared to the
No Change category, it indicates that new developments tend
to be closer to highways. On the other hand, the mean
lagDevelopment is significantly lower for the
New Development category compared to the
No Change category, it may imply that new development is
more likely to occur in areas that are near existing development.
dat %>%
dplyr::select(distance_highways,lagDevelopment,lc_change) %>%
gather(Variable, Value, -lc_change, -geometry) %>%
ggplot(., aes(lc_change, Value, fill=lc_change)) +
geom_bar(position = "dodge", stat = "summary", fun.y = "mean") +
facet_wrap(~Variable, scales = "free_y") +
scale_fill_manual(values = palette2,
labels=c("No Change","New Development"),
name="") +
labs(title="New Development as a Function of the Continuous Variables") +
plotTheme

Next, the same visualization is created for the population related
variables. The higher mean values for total_population,
total_population2020, and pop_Change_00_20 in
the New Development category suggest that development is
more likely to occur in areas with higher populations and greater
population growth.
dat %>%
dplyr::select(total_population,total_population2020,pop_Change_00_20,lc_change) %>%
gather(Variable, Value, -lc_change, -geometry) %>%
ggplot(., aes(lc_change, Value, fill=lc_change)) +
geom_bar(position = "dodge", stat = "summary", fun.y = "mean") +
facet_wrap(~Variable) +
scale_fill_manual(values = palette2,
labels=c("No Change","New Development"),
name="") +
labs(title="New Development as a Function of Factor Variables") +
plotTheme

Next, a table of land cover conversion between 2001 and 2019 is
created. The table suggests for instance, that 0.71% of The plots above
suggest that the continuous variable (e.g.,
distance_highways, lagDevelopment, population
related variables) has an association with the occurrence of new
development.
Next, a table of land cover conversion between 2001 and 2019 is
created. The table suggests for instance, that 0.77% of farmland
regionally was converted to development between 2001 and 2019.
dat %>%
dplyr::select(lc_change:otherUndeveloped,developed) %>%
gather(Land_Cover_Type, Value, -lc_change, -geometry) %>%
st_set_geometry(NULL) %>%
group_by(lc_change, Land_Cover_Type) %>%
summarize(n = sum(as.numeric(Value))) %>%
ungroup() %>%
mutate(Conversion_Rate = paste0(round(100 * n/sum(n), 2), "%")) %>%
filter(lc_change == 1) %>%
dplyr::select(Land_Cover_Type,Conversion_Rate) %>%
kable() %>% kable_styling(full_width = F)
|
Land_Cover_Type
|
Conversion_Rate
|
|
developed
|
6.07%
|
|
farm
|
0.71%
|
|
forest
|
0.43%
|
|
otherUndeveloped
|
0%
|
|
wetlands
|
0%
|
4. Predicting for 2010
In this section, six separate logistic regression models are
estimated to predict development change between 2001 and 2011 - with
each subsequent model more sophisticated then the last. To do so, the
data is split into 50% training/test sets. Models are estimated on the
training set.
Normally, as in previous chapters, a results table row would be
generated for each model describing the accuracy and generalizability of
predictions for each specification. For brevity, a less sophisticated
approach is taken here, judging each by the McFadden or “Psuedo” R
Squared statistic on the test set. The model with the greatest goodness
of fit is then used for the purposes of prediction.
4.2. Modeling
First, dat is split into training and test sets. Note
how imbalanced the panel is with
table(datTrain$lc_change1).
set.seed(3456)
trainIndex <-
createDataPartition(dat$developed, p = .50,
list = FALSE,
times = 1)
datTrain <- dat[ trainIndex,]
datTest <- dat[-trainIndex,]
nrow(dat)
## [1] 84086
Next six separate glm models are estimated adding new
variables for each. Figure 4.1 shows the Psuedo R-Squared associated
with each model.
Model1 includes only the 2001 land cover types.
Model2 adds the lagDevelopment. Models 3, 4
and 5 attempt three different approaches for modeling demographic
changes, infrastructure and slope. Model3 uses population
in 2000 and distance to highway; Model4 adds 2020 slope on
top of Model3; Model5 adds population change; and
Model6 adds demographic features. All are significant so
which population feature should be chosen? The answer lies in how the
model will be used to forecast.
Model1 <- glm(lc_change ~ wetlands + forest + farm + otherUndeveloped,
family="binomial"(link="logit"), data = datTrain)
Model2 <- glm(lc_change ~ wetlands + forest + farm + otherUndeveloped + lagDevelopment,
family="binomial"(link="logit"), data = datTrain)
Model3 <- glm(lc_change ~ wetlands + forest + farm + otherUndeveloped + lagDevelopment + total_population +
distance_highways,
family="binomial"(link="logit"), data = datTrain)
Model4 <- glm(lc_change ~ wetlands + forest + farm + otherUndeveloped + lagDevelopment + total_population +
distance_highways + slope,
family="binomial"(link="logit"), data = datTrain)
Model5 <- glm(lc_change ~ wetlands + forest + farm + otherUndeveloped + lagDevelopment + total_population +
distance_highways + slope + pop_Change_00_20 + total_population2020,
family="binomial"(link="logit"), data = datTrain)
Model6 <- glm(lc_change ~ wetlands + forest + farm + otherUndeveloped + lagDevelopment +
distance_highways + slope + pop_Change_00_20
+ total_housing_units + total_white,
family="binomial"(link="logit"), data = datTrain)
Below codes create a data frame of psudeo R Squares for each model
and plotting them for comparison. This approach loops through the models
retrieving the goodness of fit for each. Model6 is the
final model employed for prediction.
modelList <- paste0("Model", 1:6)
map_dfc(modelList, function(x)pR2(get(x)))[4,] %>%
setNames(paste0("Model",1:6)) %>%
gather(Model,McFadden) %>%
ggplot(aes(Model,McFadden)) +
geom_bar(stat="identity") +
labs(title= "McFadden R-Squared by Model") +
plotTheme
## fitting null model for pseudo-r2
## fitting null model for pseudo-r2
## fitting null model for pseudo-r2
## fitting null model for pseudo-r2
## fitting null model for pseudo-r2
## fitting null model for pseudo-r2

Next, a data frame is created that includes columns for the observed
development change, lc_change, and one that includes
predicted probabilities for Model6. This data frame is then
used as an input to a density plot visualizing the distribution of
predicted probabilities by observed class. Only a small number of
predicted probabilities are greater than or equal to 50%
(nrow(filter(testSetProbs, probs >= .50)) / nrow(datTest)).
This makes good sense, given how rare of an event development is in our
dataset. Ultimately, in order to judge our model with a confusion
matrix, a smaller development classification threshold must be
employed.
testSetProbs <-
data.frame(class = datTest$lc_change,
probs = predict(Model6, datTest, type="response"))
ggplot(testSetProbs, aes(probs)) +
geom_density(aes(fill=class), alpha=0.5) +
scale_fill_manual(values = palette1,
labels=c("No Change","New Development")) +
labs(title = "Histogram of test set predicted probabilities",
x="Predicted Probabilities",y="Density") +
plotTheme

4.3. Accuracy
Now to pick a predicted probability threshold to classify an area as
having new development. Sensitivity or the True Positive rate
is the proportion of actual positives (1’s) that were predicted to be
positive. For example, the Sensitivity in our model is the rate of
developed areas actually predicted as such. Specificity or True
Negative Rate is the proportion of actual negatives (0’s) that were
predicted to be negatives. For example, the Specificity in our model is
the rate of No Change areas that were correctly predicted as No
change.
There are some clear tradeoffs between Sensitivity and Specificity in
our model that deserve some exploration. To illustrate, two different
thresholds of 13% and 30% are explored. Predicted classes for both
thresholds are generated and instead of using the
confusionMatrix function from caret as we have
in the past, here confusion matrix metrics are derived from the
yardstick package. This allows us to group_by
the threshold and summarize the metrics of interest.
The options call below is required to tell
yardstick that the positive factor class in
testSetProbs is 1. Without it, yardstick will
by default, see the first factor level as 0 and flip the
confusion metrics around.
options(yardstick.event_first = FALSE)
testSetProbs <-
testSetProbs %>%
mutate(predClass_05 = as.factor(ifelse(testSetProbs$probs >= 0.05 ,1,0)),
predClass_20 = as.factor(ifelse(testSetProbs$probs >= 0.2 ,1,0)))
testSetProbs %>%
dplyr::select(-probs) %>%
gather(Variable, Value, -class) %>%
group_by(Variable) %>%
summarize(Sensitivity = round(yardstick::sens_vec(class,factor(Value)),2),
Specificity = round(yardstick::spec_vec(class,factor(Value)),2),
Accuracy = round(yardstick::accuracy_vec(class,factor(Value)),2)) %>%
kable() %>%
kable_styling(full_width = F)
|
Variable
|
Sensitivity
|
Specificity
|
Accuracy
|
|
predClass_05
|
0.89
|
0.68
|
0.69
|
|
predClass_20
|
0.49
|
0.94
|
0.91
|
The 13% threshold correctly predicts a higher number of new
development areas (Sensitivity), but incorrectly predicts a lower number
of no change areas (Specificity). As there are far more no change areas
in the data, this is reflected in a lower overall accuracy. Conversely,
the 30% threshold has a lower Sensitivity rate and but a far higher
Specificity rate. Again, because of the dataset is majority no change
areas, this leads to a far higher Accuracy rate.
Given the use case, and the spatial distribution of land cover
change, it may be more useful to have a model that predicts generally
where new development occurs rather than one that predicts precisely
where. As illustrated below, the 30% threshold provides this outcome.
These trade-offs can be visualized in the plot below. Here the model is
used to predict for the entire dat dataset. 30% threshold
looks more reasonable given the distribution of observed development
change.
predsForMap <-
dat %>%
mutate(probs = predict(Model6, dat, type="response") ,
Threshold_5_Pct = as.factor(ifelse(probs >= 0.05 ,1,0)),
Threshold_20_Pct = as.factor(ifelse(probs >= 0.20 ,1,0))) %>%
dplyr::select(lc_change,Threshold_5_Pct,Threshold_20_Pct) %>%
gather(Variable,Value, -geometry) %>%
st_cast("POLYGON")
ggplot() +
geom_point(data=predsForMap, aes(x=xyC(predsForMap)[,1], y=xyC(predsForMap)[,2], colour=Value)) +
facet_wrap(~Variable) +
scale_colour_manual(values = palette2, labels=c("No Change","New Development"),
name="") +
labs(title="Development Predictions - Low Threshold") +
mapTheme

To provide a bit more insight, the code block below produces both
true positives (Sensitivity) and true negatives (Specificity) for each
grid cell by threshold type. Notice how the spatial pattern of
Sensitivity for both thresholds is relatively consistent, but the 13%
threshold misses most the study area with respect to Specificity.
ConfusionMatrix.metrics <-
dat %>%
mutate(probs = predict(Model3, dat, type="response") ,
Threshold_5_Pct = as.factor(ifelse(probs >= 0.05 ,1,0)),
Threshold_20_Pct = as.factor(ifelse(probs >= 0.20 ,1,0))) %>%
mutate(TrueP_05 = ifelse(lc_change == 1 & Threshold_5_Pct == 1, 1,0),
TrueN_05 = ifelse(lc_change == 0 & Threshold_5_Pct == 0, 1,0),
TrueP_20 = ifelse(lc_change == 1 & Threshold_20_Pct == 1, 1,0),
TrueN_20 = ifelse(lc_change == 0 & Threshold_20_Pct == 0, 1,0)) %>%
dplyr::select(., starts_with("True")) %>%
gather(Variable, Value, -geometry) %>%
st_cast("POLYGON")
ggplot(data=ConfusionMatrix.metrics) +
geom_point(aes(x=xyC(ConfusionMatrix.metrics)[,1],
y=xyC(ConfusionMatrix.metrics)[,2], colour = as.factor(Value))) +
facet_wrap(~Variable) +
scale_colour_manual(values = palette2, labels=c("Correct","Incorrect"),
name="") +
labs(title="Development Predictions - Low Threshold") + mapTheme

5. Predicting Land Cover Demand for 2040
At this point, a simple but useful model has been trained to predict
urban development between 2001 and 2019 as a function of baseline
features from 2001 including land cover, built environment and
population. Next, we are going to update our features to reflect a 2019
baseline. Having done so, predictions from our new model would then be
for 2040.
For brevity, we only update two features in our model for now. First,
population change (pop_change) is updated using county
level population projections visualized in the plot below. The second is
lagDevelopment, which describes how predicted new
development relates in space to old development.
Once the features are updated, 2040 predictions are estimated and
mapped.
Below, lagDevelopment is mutate describing average
distance to 2019 development. Note that the field name,
lagDevelopment is unchanged (ie. not updated to
lagDevelopment_2019). This is done purposefully as model6
has a regression coefficient called lagDevelopment. If this
variable wasn’t present in our updated data frame then the
predict command would fail.
dat <-
dat %>%
mutate(lagDevelopment = nn_function(xyC(.), xyC(filter(.,developed10 == 2)),2))
Now to update population change. A new data frame,
countyPopulation_2040 is created which includes 2020
population counts and 2040 projections for each county in the study
area. Population is plotted by year and by county.
countyPopulation_2040 <-
data.frame(
NAME =
c("Albemarle","Charlottesville"),
county_projection_2040 =
c(138523,48939)) %>%
left_join(
dat %>%
st_set_geometry(NULL) %>%
group_by(NAME) %>%
summarize(county_population_2020 = round(sum(total_population2020))))
countyPopulation_2040 %>%
gather(Variable,Value, -NAME) %>%
ggplot(aes(reorder(NAME,-Value),Value)) +
geom_bar(aes(fill=Variable), stat = "identity", position = "dodge") +
scale_fill_manual(values = palette2,
labels=c("2040","2020"),
name="Population") +
labs(title="Population Change by County: 2020 - 2040",
x="County", y="Population") +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
plotTheme

5.2. Predicting Development Demand
Next, the countyPopulation_2040 table is joined to
dat and pop_change in order to ‘distribute’
the new population across the study area. To do so, the the allocation
of new population is weighted by a grid cell’s existing population
(pop_2040.infill). 2020 population is subtracted from this
figure to get pop_Change. Finally, Model6 is
used to predict for 2040 given the updated population change and lag
development features.
The map of predicted probabilities that results is best thought of as
a measure of predicted development demand in 2040.
dat_infill <-
dat %>%
#calculate population change
left_join(countyPopulation_2040) %>%
mutate(proportion_of_county_pop = total_population2020 / county_population_2020,
pop_2040.infill = proportion_of_county_pop * county_projection_2040,
pop_Change = round(pop_2040.infill - total_population2020),2) %>%
dplyr::select(-county_projection_2040, -county_population_2020,
-proportion_of_county_pop, -pop_2040.infill) %>%
#add values for 2020 baseline (forest, farm, etc.)
#predict for 2040
mutate(predict_2040.infill = predict(Model3,. , type="response"))
# dat_infill <- dat_infill %>%
# mutate(predict_2040.infill2 =ifelse(predict_2040.infill < 0.01 | predict_2040.infill > 1, 0, predict_2040.infill))
dat_infill %>%
ggplot() +
geom_sf(aes(fill = predict_2040.infill), color = "transparent") +
scale_fill_gradient(low = palette5[1],
high = palette5[length(palette5)],
name = "Stretched\nValues") +
geom_sf(data = studyAreaCounties, fill = NA, colour = "black", size = 1) +
labs(title = "Development Demand in 2040: Predicted Probabilities") +
mapTheme

6. Comparing Predicted Development Demand & Environmental
Sensitivity
We now have a really strong indicator of development demand for 2020
to help guide local land use planning. Demand however, is only one side
of the equation. It must balanced with the supply of environmentally
sensitive land. Understanding the interplay between demand and supply is
the first stage of the ‘Allocation’ phase, where Planners ultimately
decide which land should be developed and which should not.
For this analysis farmland and undeveloped land are be deemed
Suitable, while environmentally sensitive areas like
wetlands and forest are be deemed Not Suitable. Below, 2019
land cover data is read in and several measures of environmental
sensitivity are created by county. These include:
- The total amount of wetlands and forest land cover area in
2019.
- The amount of sensitive land (wetland and forest) lost between 2001
and 2019.
- The total area of large sensitive landscape ‘patches’ in 2019.
The third metric warrants some further discussion. In the context of
leapfrog development, Section 2.6 discusses the concept of landscape
fragmentation - the idea that discontinuous development across space
carves out disjointed slivers of wilderness. This fragmentation reduces
biodiversity particularly for species that need room to roam. Below,
environmentally sensitive_regions are created to represent
large areas of unfragmented natural resources. We then consider the
total area of these clumps for each county.
6.2. 2011 Land Cover Data
To begin, the 2019 Land Cover data is read in and reclassified.
# Read raster data
lc_2019 <- raster("https://raw.githubusercontent.com/Yingtong-Z/sprawl-forecasting/b528d725eaea5dd778c41fa644b5e6f71d6291b2/data/Cville_LC_2019.tif")
ggplot() +
geom_raster(data = rbind(rast(lc_2001) %>% mutate(label = "2001"),
rast(lc_2019) %>% mutate(label = "2019")) %>%
na.omit %>% filter(value > 0),
aes(x,y,fill=as.factor(value))) +
geom_sf(data = studyAreaCounties, fill = NA, colour = "red", size = 1) +
facet_wrap(~label) +
scale_fill_viridis(discrete=TRUE, name ="") +
labs(title = "Land Cover, 2001 & 2019") +
mapTheme + theme(legend.position = "none")

Next, each raster is aggregated to the fishnet using the
aggregateRaster function and 2019 land cover types are
mapped.
lc_2019 <- raster("https://raw.githubusercontent.com/Yingtong-Z/sprawl-forecasting/b528d725eaea5dd778c41fa644b5e6f71d6291b2/data/Cville_LC_2019.tif")
developed19 <- lc_2019 == 21 | lc_2019 == 22 | lc_2019 == 23 | lc_2019 == 24
forest19 <- lc_2019 == 41 | lc_2019 == 42 | lc_2019 == 43
farm19 <- lc_2019 == 81 | lc_2019 == 82
wetlands19 <- lc_2019 == 90 | lc_2019 == 95
otherUndeveloped19 <- lc_2019 == 52 | lc_2019 == 71 | lc_2019 == 31
water19 <- lc_2019 == 11
names(developed19) <- "developed19"
names(forest19) <- "forest19"
names(farm19) <- "farm19"
names(wetlands19) <- "wetlands19"
names(otherUndeveloped19) <- "otherUndeveloped19"
names(water19) <- "water19"
theRasterList19 <- c(developed19,forest19,farm19,wetlands19,otherUndeveloped19,water19)
###assign lc values based on hierarchy
dat2 <-
aggregateRaster(theRasterList19, dat_infill) %>%
dplyr::select(developed19,forest19,farm19,wetlands19,otherUndeveloped19,water19) %>%
st_set_geometry(NULL) %>%
bind_cols(.,dat) %>%
st_sf() %>%
st_cast("POLYGON")
# reassign cells with more than one lc value to just one based on hierarchy shown here
dat2 <- dat2 %>%
mutate(farm19 = ifelse(developed19 == 0 & farm19 == 1, 1, 0)) %>%
mutate(forest19 = ifelse(developed19 == 0 & farm19 == 0 & forest19 == 1, 1, 0)) %>%
mutate(wetlands19 = ifelse(developed19 == 0 & farm19 == 0 & forest19 == 0 & wetlands19 == 1, 1, 0)) %>%
mutate(water19 = ifelse(developed19 == 0 & farm19 == 0 & forest19 == 0 & wetlands19 == 0 & water19 == 1, 1, 0)) %>%
mutate(otherUndeveloped19 = ifelse(developed19 == 0 & farm19 == 0 & forest19 == 0 & wetlands19 == 0 & water19 == 0 & otherUndeveloped19 == 1, 1, 0))
# ###assign lc values based on most common lc type within each fishnet cell
# dat2 <-
# aggregateRaster(theRasterList19, dat_infill) %>%
# dplyr::select(developed19,forest19,farm19,wetlands19,otherUndeveloped19,water19) %>%
# st_set_geometry(NULL) %>%
# mutate(across(everything(), as.numeric)) %>% # Convert all columns to numeric
# mutate(max_col = apply(., 1, which.max)) %>% # Find the index of the maximum value in each row
# mutate(across(everything(), ~replace(., row_number() != max_col, 0), .names = "new_{.col}")) %>% # Replace non-maximum values with 0
# dplyr::select(new_developed19:new_water19) %>%
# bind_cols(.,dat) %>%
# st_sf() %>%
# st_cast("POLYGON")
# #rename columns to max names
# dat2 <- dat2 %>%
# mutate(developed19 = new_developed19) %>%
# mutate(forest19 = new_forest19) %>%
# mutate(farm19 = new_farm19) %>%
# mutate(wetlands19 = new_wetlands19) %>%
# mutate(otherUndeveloped19 = new_otherUndeveloped19) %>%
# mutate(water19 = new_water19)
dat2 %>%
gather(var,value,developed19:water19) %>%
st_centroid() %>%
mutate(X = st_coordinates(.)[,1],
Y = st_coordinates(.)[,2]) %>%
ggplot() +
geom_sf(data=CvilleMSA) +
geom_point(aes(X,Y, colour=as.factor(value)), size = 0.1) +
facet_wrap(~var) +
scale_colour_manual(values = palette2,
labels=c("Other","Land Cover"),
name = "") +
labs(title = "Land Cover Types, 2019",
subtitle = "As fishnet centroids") +
mapTheme

Reformat 2019 Land Cover and other features for Model, preserving
2001 features as new columns.
dat2 <- dat2 %>%
# 2001 land cover
mutate(farm01 = farm) %>%
mutate(forest01 = forest) %>%
mutate(wetlands01 = wetlands) %>%
mutate(water01 = water) %>%
mutate(otherUndeveloped01 = otherUndeveloped) %>%
mutate(total_housing_units01 = total_housing_units) %>%
mutate(total_white01 = total_white) %>%
# 2019 land cover
mutate(farm = farm19) %>%
mutate(forest = forest19) %>%
mutate(wetlands = wetlands19) %>%
mutate(water = water19) %>%
mutate(otherUndeveloped = otherUndeveloped19) %>%
mutate(total_housing_units = total_housing_units2020) %>%
mutate(total_white = total_white2020)
6.3. Sensitive Land Cover Lost
Below an indicator sensitive_lost is created indicating
grid cells that were either forest or wetlands in 2001 but were no
longer so in 2019. The output layer, sensitive_land_lost,
gives a sense for how development in the recent past has effected the
natural environment.
dat2 <-
dat2 %>%
mutate(sensitive_lost19 = ifelse(forest01 == 1 & forest19 == 0 |
wetlands01 == 1 & wetlands19 == 0,1,0))
ggplot() +
geom_sf(data=dat2, aes(fill =as.factor(sensitive_lost19)), color = "transparent") +
scale_fill_manual(values = palette2,
labels=c("No Change","Sensitive Lost"),
name = "") +
labs(title = "Sensitive lands lost: 2001 - 2019") +
mapTheme

6.4 Landscape Fragmentation
In this section, the wetlands11 and
forest11 rasters are converted to contiguous
sensitive_regions using the raster::clump
function. This is equivalent to Region Group in ArcGIS. The raster
clumps are then converted to vector sf layers; dissolved
into unique regions; Acres are calculated; and the layers are converted
back to raster to be extracted back to the fishnet with
aggregateRaster. Note that only
sensitive_regions with areas greater than 1 acre are
included.
sensitiveRegions <-
raster::clump(wetlands19 + forest19) %>%
rasterToPolygons() %>%
st_as_sf() %>%
group_by(clumps) %>%
summarize() %>%
mutate(Acres = as.numeric(st_area(.) * 0.0000229568)) %>%
filter(Acres > 3954) %>%
dplyr::select() %>%
raster::rasterize(.,emptyRaster)
sensitiveRegions[sensitiveRegions > 0] <- 1
names(sensitiveRegions) <- "sensitiveRegions"
dat2 <-
aggregateRaster(c(sensitiveRegions), dat2) %>%
dplyr::select(sensitiveRegions) %>%
st_set_geometry(NULL) %>%
bind_cols(.,dat2) %>%
st_sf()
ggplot() +
geom_sf(data=dat2, aes(fill =as.factor(sensitiveRegions)), color = "transparent") +
scale_fill_manual(values = palette2,
labels=c("Other","Sensitive Regions"),
name="") +
labs(title = "Sensitive regions",
subtitle = "Continous areas of either wetlands or forests\ngreater than 1 acre") +
mapTheme

6.5. Summarize by County
The below dplyr statement takes as its input,
dat2, which was created in Sections 6.2 - 6.4 and wrangles
together a table of county-level, supply and demand metrics which can be
used to analyze suitability by county.
county_specific_metrics <-
dat2 %>%
#predict development demand from our model
mutate(Development_Demand = predict(Model6, dat2, type="response")) %>%
#get a count count of grid cells by county which we can use to calculate rates below
left_join(st_set_geometry(dat, NULL) %>% group_by(NAME) %>% summarize(count = n())) %>%
#calculate summary statistics by county
group_by(NAME) %>%
summarize(Total_Farmland = sum(farm19) / max(count),
Total_Forest = sum(forest19) / max(count),
Total_Wetlands = sum(wetlands19) / max(count),
Total_Undeveloped = sum(otherUndeveloped19) / max(count),
Sensitive_Land_Lost = sum(sensitive_lost19) / max(count),
Sensitive_Regions = sum(sensitiveRegions) / max(count),
Mean_Development_Demand = mean(Development_Demand)) %>%
#get population data by county
left_join(countyPopulation_2040 %>%
mutate(Population_Change = county_projection_2040 - county_population_2020,
Population_Change_Rate = Population_Change / county_projection_2040) %>%
dplyr::select(NAME,Population_Change_Rate))
Now a small multiple plot can be created providing both supply and
demand side analytics by county. The plot gives a sense for development
demand (Demand-Side), suitable land for development
(Suitable) and sensitive land
(Not Suitable).
The data suggests both population and development demand will
increase for Charlottesville MSA. However, compared to Charlottesville
county, Albemarle has a high rate of developable farmland and a low
supply of sensitive land. Albemarleort is well suitable to new
development than Charlottesville, where the latter has lower supply of
developable lands.
county_specific_metrics %>%
gather(Variable, Value, -NAME, -geometry) %>%
mutate(Variable = factor(Variable, levels=c("Population_Change_Rate","Mean_Development_Demand",
"Total_Farmland","Total_Undeveloped","Total_Forest",
"Total_Wetlands","Sensitive_Land_Lost","Sensitive_Regions",
ordered = TRUE))) %>%
mutate(Planning_Designation = case_when(
Variable == "Population_Change_Rate" | Variable == "Mean_Development_Demand" ~ "Demand-Side",
Variable == "Total_Farmland" | Variable == "Total_Undeveloped" ~ "Suitable",
TRUE ~ "Not Suitable")) %>%
ggplot(aes(x=Variable, y=Value, fill=Planning_Designation)) +
geom_bar(stat="identity", position=position_dodge(), colour="black") +
facet_wrap(~NAME, ncol=5) +
coord_flip() +
scale_y_continuous(breaks = seq(.25, 1, by = .25)) +
geom_vline(xintercept = 2.5) + geom_vline(xintercept = 4.5) +
scale_fill_manual(values=c("black","red","darkgreen")) +
labs(title= "County Specific Allocation Metrics", subtitle= "As rates", x="Indicator", y="Rate") +
plotTheme + theme(axis.text.x = element_text(angle = 45, hjust = 1), legend.position="bottom")

7. Allocation
Allocation is the final stage of the urban growth modeling process.
Now that both demand and supply is understood, We can allocate
development rights accordingly. Of course, this could take many forms of
regulation including zoning, subdivision approval or outright
conservation. In this section, demand and supply are visualized for two
counties, Charlottesville and Albemarle The data suggests that the
latter is more conducive to growth while the former, less so.
First, development demand is predicted for Albemarle. Then a layer,
Albemarle_landUse is created, that includes indicators for
both previously developed land and environmentally unsuitable land. This
layer then is overlayed atop development demand and projected population
change to give the full supply and demand-side picture in Albemarle.
There are some clear opportunities for development in Albemarle.
Significant infill opportunities exist along the roads and highways
where population change is projected to be greatest. There is also a
good deal of environmentally suitable land along the highways. This
would be ideal space for land developments.
Albemarle <-
dat2 %>% #calculate population change
left_join(countyPopulation_2040) %>%
mutate(proportion_of_county_pop = total_population2020 / county_population_2020,
pop_2040.infill = proportion_of_county_pop * county_projection_2040,
pop_Change = (pop_2040.infill - total_population2020)) %>%
dplyr::select(-county_projection_2040, -county_population_2020,
-proportion_of_county_pop, -pop_2040.infill) %>%
mutate(Development_Demand = predict(Model6, dat2, type="response")) %>%
filter(NAME == "Albemarle")
Albemarle_landUse <- rbind(
filter(Albemarle, forest19 == 1 | wetlands19 == 1 ) %>%
dplyr::select() %>% mutate(Land_Use = "Not Suitable"),
filter(Albemarle, developed19 == 1) %>%
dplyr::select() %>% mutate(Land_Use = "Developed"))
grid.arrange(
ggplot() +
geom_sf(data=Albemarle, aes(fill=factor(ntile(Development_Demand,5))), colour=NA) +
geom_point(data=Albemarle_landUse, aes(x=xyC(Albemarle_landUse)[,1],
y=xyC(Albemarle_landUse)[,2], colour=Land_Use),
shape = 16, size = 0.3) +
geom_sf(data=st_intersection(CvilleHighways,filter(studyAreaCounties, NAME=="Albemarle")), size=2) +
scale_fill_manual(values = palette5, name="Development_Demand",
labels=substr(quintileBreaks(Albemarle,"Development_Demand"),1,5)) +
scale_colour_manual(values = c("black","red")) +
labs(title = "Development Potential, \n2040: Albemarle") + mapTheme +
guides(fill = guide_legend(order = 1), colour = guide_legend(order = 2)),
ggplot() +
geom_sf(data=Albemarle, aes(fill=factor(ntile(pop_Change,5))), colour=NA) +
geom_point(data=Albemarle_landUse, aes(x=xyC(Albemarle_landUse)[,1],
y=xyC(Albemarle_landUse)[,2], colour=Land_Use),
shape = 16, size = 0.3) +
geom_sf(data=st_intersection(CvilleHighways,filter(studyAreaCounties, NAME=="Albemarle")), size=2) +
scale_fill_manual(values = palette5, name="Population_Change",
labels=substr(quintileBreaks(Albemarle,"pop_Change"),1,5)) +
scale_colour_manual(values = c("black","red")) +
labs(title = "Projected Population, \n2040: Albemarle") + mapTheme +
guides(fill = guide_legend(order = 1), colour = guide_legend(order = 2)), ncol=2)

The plots above are created using a ggplot trick to show
what appears to be overlayed polygons (fishnet grid cells).
For comparison purposes, this process is replicated for
Charlottesville county below. There is way less available lands for
growth.
Charlottesville <-
dat2 %>% #calculate population change
left_join(countyPopulation_2040) %>%
mutate(proportion_of_county_pop = total_population2020 / county_population_2020,
pop_2040.infill = proportion_of_county_pop * county_projection_2040,
pop_Change = (pop_2040.infill - total_population2020)) %>%
dplyr::select(-county_projection_2040, -county_population_2020,
-proportion_of_county_pop, -pop_2040.infill) %>%
mutate(Development_Demand = predict(Model6, dat2, type="response")) %>%
filter(NAME == "Charlottesville")
Charlottesville_landUse <- rbind(
filter(Charlottesville, forest19 == 1 | wetlands19 == 1 ) %>%
dplyr::select() %>% mutate(Land_Use = "Not Suitable"),
filter(Charlottesville, developed19 == 1) %>%
dplyr::select() %>% mutate(Land_Use = "Developed"))
grid.arrange(
ggplot() +
geom_sf(data=Charlottesville, aes(fill=factor(ntile(Development_Demand,5))), colour=NA) +
geom_point(data=Charlottesville_landUse, aes(x=xyC(Charlottesville_landUse)[,1],
y=xyC(Charlottesville_landUse)[,2], colour=Land_Use),
shape = 16, size = 1) +
scale_fill_manual(values = palette5, name="Development_Demand",
labels=substr(quintileBreaks(Charlottesville,"Development_Demand"),1,5)) +
scale_colour_manual(values = c("black","red")) +
labs(title = "Development Potential, \n2040: Charlottesville") + mapTheme +
guides(fill = guide_legend(order = 1), colour = guide_legend(order = 2)),
ggplot() +
geom_sf(data=Charlottesville, aes(fill=factor(ntile(pop_Change,5))), colour=NA) +
geom_point(data=Charlottesville_landUse, aes(x=xyC(Charlottesville_landUse)[,1],
y=xyC(Charlottesville_landUse)[,2], colour=Land_Use),
shape = 16, size = 1) +
scale_fill_manual(values = palette5, name="Population_Change",
labels=substr(quintileBreaks(Albemarle,"pop_Change"),1,5)) +
scale_colour_manual(values = c("black","red")) +
labs(title = "Projected Population, \n2040: Charlottesville") + mapTheme +
guides(fill = guide_legend(order = 1), colour = guide_legend(order = 2)), ncol=2)

We stop short in actually allocating land to development. While the
model is well suited for understanding sprawl-style development, it is
not useful for understanding how new demand might be absorbed by
upzoning and densification of existing development. It would not be wise
to allocate the entire projected population to undeveloped land.
Instead, we’d prefer a more nuanced understanding of how local land use
laws might play a role. At this stage in the analysis however, the
Planner has all she needs to engage local stakeholders about future
development decisions.
LS0tCnRpdGxlOiAiQ2hhcmxvdHRlc3ZpbGxlIE1TQSBVcmJhbiBHcm93dGggTW9kZWxpbmciCmF1dGhvcjogIk9saXZlciBBdHdvb2QsIFlpbmd0b25nIFpob25nIgpkYXRlOiAiMDUvMDIvMjAyMyIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UpCgpybShsaXN0PWxzKCkpCmBgYAoKPHN0eWxlPgogIC5zdXBlcmJpZ2ltYWdlewogICAgICBvdmVyZmxvdy14OnNjcm9sbDsKICAgICAgd2hpdGUtc3BhY2U6IG5vd3JhcDsKICB9CgogIC5zdXBlcmJpZ2ltYWdlIGltZ3sKICAgICBtYXgtd2lkdGg6IG5vbmU7CiAgfQoKCjwvc3R5bGU+CgoKCiMgMS4gSW50cm9kdWN0aW9uClJlZ2lvbmFsIHVyYmFuIGRldmVsb3BtZW50IGlzIGluZmx1ZW5jZWQgYnkgdmFyaW91cyBzdGFrZWhvbGRlcnMgc3VjaCBhcyBkZXZlbG9wZXJzLCByZWFsIGVzdGF0ZSBidXllcnMsIHRlbmFudHMsIHBsYW5uZXJzLCBhbmQgcmVndWxhdG9ycywgZWFjaCBwdXJzdWluZyB0aGVpciBvd24gb2JqZWN0aXZlcy4gVG8gZW5zdXJlIGVjb25vbWljIHByb2R1Y3Rpdml0eSBhbmQgc3VzdGFpbmFiaWxpdHksIGxhbmQgdXNlIHBsYW5uaW5nIG11c3QgY29uc2lkZXIgYm90aCBzdXBwbHkgYW5kIGRlbWFuZC1zaWRlIGluc2lnaHRzLgoKVGhpcyBwcm9qZWN0IGZvY3VzZXMgb24gdGhlIENoYXJsb3R0ZXN2aWxsZSBNZXRyb3BvbGl0YW4gU3RhdGlzdGljYWwgQXJlYSAoTVNBKSBhcyBhIGNhc2Ugc3R1ZHksIGV4YW1pbmluZyBob3cgYSBzcHJhd2xpbmcgbWV0cm9wb2xpdGFuIGFyZWEgYmFsYW5jZXMgZWNvbm9taWMgZ3Jvd3RoIHdpdGggZW52aXJvbm1lbnRhbCBzdXN0YWluYWJpbGl0eSBieSBwcmVkaWN0aW5nIGxhbmQgY292ZXIgY2hhbmdlcy4gVGhlIHByb2plY3QgZHJhd3Mgb24gZGF0YSBmcm9tIHNvdXJjZXMgc3VjaCBhcyB0aGUgVVMgR2VvbG9naWNhbCBTdXJ2ZXkgKFVTR1MpLCBDZW5zdXMgZGVtb2dyYXBoaWNzLCBhbmQgdHJhbnNwb3J0YXRpb24sIGFzIHdlbGwgYXMgc3BhdGlhbCBsYWcgZmVhdHVyZXMgZnJvbSBsYW5kIGNvdmVyIGNoYW5nZSwgdG8gYmV0dGVyIHVuZGVyc3RhbmQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGRldmVsb3BtZW50IGRlbWFuZCBhbmQgZW52aXJvbm1lbnRhbGx5IHNlbnNpdGl2ZSBsYW5kLgoKVGhyb3VnaCBleHBsb3JhdG9yeSBhbmFseXNpcywgIGEgZ2Vvc3BhdGlhbCBwcmVkaWN0aXZlIG1vZGVsIGlzIGRldmVsb3BlZCBieSB0cmFpbmluZyBvbiBsYW5kIGNvdmVyIGNoYW5nZXMgZnJvbSAyMDAxLTIwMTkuIFRoaXMgbW9kZWwgaXMgdXNlZCB0byBlc3RpbWF0ZSBkZXZlbG9wbWVudCBkZW1hbmQgZm9yIHRoZSB5ZWFyIDIwNDAsIHdoaWxlIGNvbnNpZGVyaW5nIHRoZSBpbXBhY3Qgb24gdGhlIGVudmlyb25tZW50IGFuZCBsYW5kc2NhcGUgZnJhZ21lbnRhdGlvbi4gVGhlIG1vZGVsJ3MgcHJlZGljdGlvbnMgYXJlIHRoZW4gdXNlZCB0byBndWlkZSBuZXcgZGV2ZWxvcG1lbnQgaW4gYXJlYXMgdGhhdCBzdXBwb3J0IGVjb25vbWljIGdyb3d0aCB3aXRob3V0IGNvbXByb21pc2luZyBzdXN0YWluYWJpbGl0eSBnb2Fscy4KCiMgMS4yLiBTZXR1cAoKQmVsb3cgd2UgbG9hZCByZXF1aXJlZCBsaWJyYXJpZXMsIG1hcFRoZW1lIGFuZCBwbG90VGhlbWUgZm9yIGNvbnNpc3RlbnQgc3R5bGluZywgYW5kIHNwZWNpZnkgYSBwYWxldHRlIG9mIGNvbG9ycyBmb3IgdmlzdWFsaXphdGlvbnMKCmBgYHtyIGxvYWRfcGFja2FnZXMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHMgPSAiaGlkZSJ9CmxpYnJhcnkodGlncmlzKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShzZikKbGlicmFyeShyYXN0ZXIpCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeSh0aWR5Y2Vuc3VzKQpsaWJyYXJ5KHRpZ3JpcykKbGlicmFyeShGTk4pCiNsaWJyYXJ5KFF1YW50UHN5YykgIyBKRSBOb3RlOiBpbiBSIDQuMSwgUXVhbnRQc3ljIHBhY2thZ2Ugbm90IGF2YWlsYWJsZS4KbGlicmFyeShjYXJldCkKbGlicmFyeSh5YXJkc3RpY2spCmxpYnJhcnkocHNjbCkKbGlicmFyeShwbG90Uk9DKSAKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KHBST0MpCmxpYnJhcnkoZ3JpZCkKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkodmlyaWRpcykKbGlicmFyeShpZ3JhcGgpCmxpYnJhcnkobWFwdmlldykKbGlicmFyeShleGFjdGV4dHJhY3RyKQoKCnBsb3RUaGVtZSA8LSB0aGVtZSgKICBwbG90LnRpdGxlID1lbGVtZW50X3RleHQoc2l6ZT0xMiksCiAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTgpLAogIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChzaXplID0gNiksCiAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLAogIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgIyBTZXQgdGhlIGVudGlyZSBjaGFydCByZWdpb24gdG8gYmxhbmsKICBwYW5lbC5iYWNrZ3JvdW5kPWVsZW1lbnRfYmxhbmsoKSwKICBwbG90LmJhY2tncm91bmQ9ZWxlbWVudF9ibGFuaygpLAogICNwYW5lbC5ib3JkZXI9ZWxlbWVudF9yZWN0KGNvbG91cj0iI0YwRjBGMCIpLAogICMgRm9ybWF0IHRoZSBncmlkCiAgcGFuZWwuZ3JpZC5tYWpvcj1lbGVtZW50X2xpbmUoY29sb3VyPSIjRDBEMEQwIixzaXplPS43NSksCiAgYXhpcy50aWNrcz1lbGVtZW50X2JsYW5rKCkpCgptYXBUaGVtZSA8LSB0aGVtZShwbG90LnRpdGxlID1lbGVtZW50X3RleHQoc2l6ZT0xMiksCiAgICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICAgICAgICAgICAgcGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KHNpemUgPSA2KSwKICAgICAgICAgICAgICAgICAgYXhpcy5saW5lPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgIGF4aXMudGlja3M9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnk9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgcGFuZWwuYm9yZGVyPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgcGFuZWwuZ3JpZC5tYWpvcj1lbGVtZW50X2xpbmUoY29sb3VyID0gJ3RyYW5zcGFyZW50JyksCiAgICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWlub3I9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gInZlcnRpY2FsIiwgCiAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsCiAgICAgICAgICAgICAgICAgIHBsb3QubWFyZ2luID0gbWFyZ2luKDEsIDEsIDEsIDEsICdjbScpLAogICAgICAgICAgICAgICAgICBsZWdlbmQua2V5LmhlaWdodCA9IHVuaXQoMSwgImNtIiksIGxlZ2VuZC5rZXkud2lkdGggPSB1bml0KDAuMiwgImNtIikpCgpwYWxldHRlMSA8LSBjKCIjY2QwMDAwIiwiIzAwNTljNyIpCnBhbGV0dGUyIDwtIGMoIiM0MWI2YzQiLCIjMjUzNDk0IikKcGFsZXR0ZTQgPC0gYygiI2ExZGFiNCIsIiM0MWI2YzQiLCIjMmM3ZmI4IiwiIzI1MzQ5NCIpCnBhbGV0dGU1IDwtIGMoIiNmZmZmY2MiLCIjYTFkYWI0IiwiIzQxYjZjNCIsIiMyYzdmYjgiLCIjMjUzNDk0IikKcGFsZXR0ZTEwIDwtIGMoIiNmN2ZjZjAiLCIjZTBmM2RiIiwiI2NjZWJjNSIsIiNhOGRkYjUiLCIjN2JjY2M0IiwKICAgICAgICAgICAgICAgIiM0ZWIzZDMiLCIjMmI4Y2JlIiwiIzA4NjhhYyIsIiMwODQwODEiLCIjZjdmY2YwIikKYGBgCgpXZSBhbHNvIGluY2x1ZGUgc2V2ZXJhbCBoZWxwZXIgZnVuY3Rpb25zLiBgcXVpbnRpbGVzQnJlYWtzYCB0YWtlcyBhIGRhdGFmcmFtZSBhbmQgYSBjb2x1bW4gYW5kIG91dHB1dHMgdGhlIHF1aW50aWxlcyBicmVha3MsIGhlbHBpbmcgc2hvcnRlbiB0aGUgYmVsb3cgYGdncGxvdGAgY2FsbHMuCgpJdCB0YWtlcyBsb25nZXIgdG8gYGdncGxvdGAgYSBwb2x5Z29uIGZpc2huZXQgd2l0aCBgZ2VvbV9zZmAgdGhhbiBpdCBkb2VzIHRvIHBsb3QgYGdlb21fcG9pbnRgLiBUbyBjdXQgZG93biBvbiBwbG90dGluZyB0aW1lLCB0aGUgYHh5Q2AgKGZvciDigJhYWSBDb29yZGluYXRlc+KAmSkgdGFrZXMgYSBmaXNobmV0IGBzZmAgYW5kIGNvbnZlcnRzIGl0IHRvIGEgZGF0YWZyYW1lIG9mIGdyaWQgY2VsbCBjZW50cm9pZCBjb29yZGluYXRlcy4KCmByYXN0YCBpcyBhIGZ1bmN0aW9uIGFsbG93aW5nIHVzIHRvIHF1aWNrbHkgcGxvdCByYXN0ZXIgdmFsdWVzIGluIGBnZ3Bsb3RgLgoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQojdGhpcyBmdW5jdGlvbiBjb252ZXJ0cyBhIGNvbHVtbiBpbiB0byBxdWludGlsZXMuIEl0IGlzIHVzZWQgZm9yIG1hcHBpbmcuCnF1aW50aWxlQnJlYWtzIDwtIGZ1bmN0aW9uKGRmLHZhcmlhYmxlKSB7CiAgICBhcy5jaGFyYWN0ZXIocXVhbnRpbGUoZGZbW3ZhcmlhYmxlXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgYyguMDEsLjIsLjQsLjYsLjgpLG5hLnJtPVQpKQp9CgojVGhpcyBmdW5jdGlvbiBjYW4gYmUgdXNlZCB0byBjb252ZXJ0IGEgcG9seWdvbiBzZiB0byBjZW50cm9pZHMgeHkgY29vcmRzLgp4eUMgPC0gZnVuY3Rpb24oYVBvbHlnb25TRikgewogIGFzLmRhdGEuZnJhbWUoCiAgICBjYmluZCh4PXN0X2Nvb3JkaW5hdGVzKHN0X2NlbnRyb2lkKGFQb2x5Z29uU0YpKVssMV0sCiAgICAgICAgICB5PXN0X2Nvb3JkaW5hdGVzKHN0X2NlbnRyb2lkKGFQb2x5Z29uU0YpKVssMl0pKQp9IAoKI3RoaXMgZnVuY3Rpb24gY29udmVydCBhIHJhc3RlciB0byBhIGRhdGEgZnJhbWUgc28gaXQgY2FuIGJlIHBsb3R0ZWQgaW4gZ2dwbG90CnJhc3QgPC0gZnVuY3Rpb24oaW5SYXN0ZXIpIHsKICBkYXRhLmZyYW1lKAogICAgeHlGcm9tQ2VsbChpblJhc3RlciwgMTpuY2VsbChpblJhc3RlcikpLCAKICAgIHZhbHVlID0gZ2V0VmFsdWVzKGluUmFzdGVyKSkgfQpgYGAKCgojIDIuIERhdGEgV3JhbmdsaW5nICYgRmVhdHVyZSBFbmdpbmVlcmluZwoKSW4gdGhpcyBzZWN0aW9uIGEgY29uc2lkZXJhYmxlIGFtb3VudCBvZiB2ZWN0b3IgYW5kIHJhc3RlciBkYXRhIGlzIHdyYW5nbGVkIHRvZ2V0aGVyIGludG8gYSByZWdyZXNzaW9uLXJlYWR5IGRhdGFzZXQuIFRoZSBmb2xsb3dpbmcgZGF0YXNldHMgYXJlIHVzZWQ6CgoyLjIgLSAyLjM6IExhbmQgY292ZXIgZGF0YSBbZG93bmxvYWRlZF0oaHR0cHM6Ly93d3cubXJsYy5nb3YvZGF0YS9ubGNkLWxhbmQtY292ZXItY2hhbmdlLWluZGV4LWNvbnVzKSBmcm9tIHRoZSBNdWx0aS1SZXNvbHV0aW9uIExhbmQgQ2hhcmFjdGVyaXN0aWNzIENvbnNvcnRpdW3igJlzIE5hdGlvbmFsIExhbmQgQ292ZXIgRGF0YWJhc2UgKE5MQ0QpIGluY2x1ZGVzIGFubnVhbCBsYW5kIGNvdmVyIGFuZCBsYW5kIGNvdmVyIGNoYW5nZSByYXN0ZXIgZGF0YSBmb3IgdGhlIENoYXJsb3R0ZXN2aWxsZSBhbmQgQWxiZW1hcmxlIGNvdW50cmllcy4gVGhlc2UgZGF0YSBhcmUgc2FtcGxlZCB0byBhIDQsMDAwIGJ5IDQsMDAwIGZ0XjIgZmlzaG5ldC4KCjIuNDogUG9wdWxhdGlvbiBkYXRhIGlzIGRvd25sb2FkZWQgZnJvbSB0aGUgVS5TLiBDZW5zdXMgYW5kIGpvaW5lZCB0byB0aGUgZmlzaG5ldCBieSBkaXN0cmlidXRpbmcgQmxvY2sgcG9wdWxhdGlvbiB0b3RhbHMgcHJvcG9ydGlvbmFsbHkgdG8gZWFjaCBncmlkIGNlbGwuCgoyLjU6IEhpZ2h3YXkgdmVjdG9ycyBhcmUgZG93bmxvYWRlZCBmcm9tIFZpcmdpbmlhIFN0YXRlIG9wZW4gZGF0YSB3ZWJzaXRlIGFuZCB1c2VkIHRvIHdyYW5nbGUgaGlnaHdheSBwcm94aW1pdHkgZmVhdHVyZXMuCgoyLjY6IFRoZSBsYW5kIGNvdmVyIGNoYW5nZSBkYXRhIGlzIHVzZWQgdG8gZW5naW5lZXIgc3BhdGlhbCBsYWcgZmVhdHVyZXMuCgoyLjc6IENvdW50eSBwb2x5Z29ucyBhcmUgZG93bmxvYWRlZCB1c2luZyB0aGUgYHRpZ3Jpc2AgcGFja2FnZS4KCjIuODogRWFjaCBmZWF0dXJlIGlzIHdyYW5nbGVkIGludG8gYSBmaW5hbCBkYXRhc2V0LgoKT3RoZXIgcmFzdGVyIGZlYXR1cmVzIGFyZSBjcmVhdGVkIHN1Y2ggYXMgZGlzdGFuY2UgdG8gaGlnaHdheXMsIGZvciBpbnN0YW5jZS4gVGhlc2UgcmFzdGVycyBhcmUgdGhlbiBpbnRlZ3JhdGVkIHdpdGggYSB2ZWN0b3IgZmlzaG5ldC4gQWRkaXRpb25hbCBmZWF0dXJlIGVuZ2luZWVyaW5nIGlzIHBlcmZvcm1lZCBvbiB0aGUgdmVjdG9yLXNpZGUgcHJvdmlkaW5nIGEgc2ltcGxlLCBidXQgY29tcHJlaGVuc2l2ZSBzbmFwc2hvdCBvZiB0aGUgZGV2ZWxvcG1lbnQgcHJvY2VzcyBpbiBhbmQgYXJvdW5kIENoYXJsb3R0ZXN2aWxsZSBNU0EgYmV0d2VlbiAyMDAxIGFuZCAyMDE5LgoKCiMjIDIuMi4gTGFuZCBDb3ZlciBDaGFuZ2UgRGF0YQoKV2UgYWltIHRvIGZvcmVjYXN0IHRoZSBkZXBlbmRlbnQgdmFyaWFibGUgb2YgbGFuZCBjb3ZlciBjaGFuZ2UgYmV0d2VlbiAyMDAxIGFuZCAyMDE5LiBJbiB0aGlzIHNlY3Rpb24sIHdlIGxvYWQgdGhlIGxhbmQgY292ZXIgcmFzdGVyIGRhdGEgYW5kIHJlY2xhc3NpZnkgaXQsIGludGVncmF0aW5nIGl0IHdpdGggYSB2ZWN0b3IgZmlzaG5ldC4gVGhpcyBhbGxvd3MgdXMgdG8gcGFyYW1ldGVyaXplIHNwYXRpYWwgcmVsYXRpb25zaGlwcyBpbiBhIHJlZ3Jlc3Npb24gY29udGV4dC4KClRoZSB0YWJsZSBiZWxvdyBzaG93cyBkZXNjcmlwdGlvbnMgb2YgZWFjaCBjYXRlZ29yaWNhbCBsYW5kIGNvdmVyIHR5cGUgaW4gdGhlIGxhbmQgY292ZXIgZGF0YS4gQmVsb3csIHdlIHdpbGwgcmVjbGFzc2lmeSB0aGVzZSBkYXRhIGludG8gbW9yZSB1c2VmdWwgY2F0ZWdvcmllcy4KCnwgQ2xhc3MgVmFsdWUgfCBDbGFzc2lmaWNhdGlvbiBEZXNjcmlwdGlvbiB8CnwgLS0tLS0tLS0tLS0gfCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSB8CnwgV2F0ZXIgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgMTEgICAgICAgICAgfCBPcGVuIFdhdGVyIC0gYXJlYXMgb2Ygb3BlbiB3YXRlciwgZ2VuZXJhbGx5IHdpdGggbGVzcyB0aGFuIDI1JSBjb3ZlciBvZiB2ZWdldGF0aW9uIG9yIHNvaWwuIHwKfCAxMiAgICAgICAgICB8IFBlcmVubmlhbCBJY2UvU25vdyAtIGFyZWFzIGNoYXJhY3Rlcml6ZWQgYnkgYSBwZXJlbm5pYWwgY292ZXIgb2YgaWNlIGFuZC9vciBzbm93LCBnZW5lcmFsbHkgZ3JlYXRlciB0aGFuIDI1JSBvZiB0b3RhbCBjb3Zlci4gfAp8IERldmVsb3BlZCAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IDIxICAgICAgICAgIHwgRGV2ZWxvcGVkLCBPcGVuIFNwYWNlIC0gYXJlYXMgd2l0aCBhIG1peHR1cmUgb2Ygc29tZSBjb25zdHJ1Y3RlZCBtYXRlcmlhbHMsIGJ1dCBtb3N0bHkgdmVnZXRhdGlvbiBpbiB0aGUgZm9ybSBvZiBsYXduIGdyYXNzZXMuIEltcGVydmlvdXMgc3VyZmFjZXMgYWNjb3VudCBmb3IgbGVzcyB0aGFuIDIwJSBvZiB0b3RhbCBjb3Zlci4gVGhlc2UgYXJlYXMgbW9zdCBjb21tb25seSBpbmNsdWRlIGxhcmdlLWxvdCBzaW5nbGUtZmFtaWx5IGhvdXNpbmcgdW5pdHMsIHBhcmtzLCBnb2xmIGNvdXJzZXMsIGFuZCB2ZWdldGF0aW9uIHBsYW50ZWQgaW4gZGV2ZWxvcGVkIHNldHRpbmdzIGZvciByZWNyZWF0aW9uLCBlcm9zaW9uIGNvbnRyb2wsIG9yIGFlc3RoZXRpYyBwdXJwb3Nlcy4gfAp8IDIyICAgICAgICAgIHwgRGV2ZWxvcGVkLCBMb3cgSW50ZW5zaXR5IC0gYXJlYXMgd2l0aCBhIG1peHR1cmUgb2YgY29uc3RydWN0ZWQgbWF0ZXJpYWxzIGFuZCB2ZWdldGF0aW9uLiBJbXBlcnZpb3VzIHN1cmZhY2VzIGFjY291bnQgZm9yIDIwJSB0byA0OSUgcGVyY2VudCBvZiB0b3RhbCBjb3Zlci4gVGhlc2UgYXJlYXMgbW9zdCBjb21tb25seSBpbmNsdWRlIHNpbmdsZS1mYW1pbHkgaG91c2luZyB1bml0cy4gfAp8IDIzICAgICAgICAgIHwgRGV2ZWxvcGVkLCBNZWRpdW0gSW50ZW5zaXR5IC0gYXJlYXMgd2l0aCBhIG1peHR1cmUgb2YgY29uc3RydWN0ZWQgbWF0ZXJpYWxzIGFuZCB2ZWdldGF0aW9uLiBJbXBlcnZpb3VzIHN1cmZhY2VzIGFjY291bnQgZm9yIDUwJSB0byA3OSUgb2YgdGhlIHRvdGFsIGNvdmVyLiBUaGVzZSBhcmVhcyBtb3N0IGNvbW1vbmx5IGluY2x1ZGUgc2luZ2xlLWZhbWlseSBob3VzaW5nIHVuaXRzLiB8CnwgMjQgICAgICAgICAgfCBEZXZlbG9wZWQgSGlnaCBJbnRlbnNpdHkgLSBoaWdobHkgZGV2ZWxvcGVkIGFyZWFzIHdoZXJlIHBlb3BsZSByZXNpZGUgb3Igd29yayBpbiBoaWdoIG51bWJlcnMuIEV4YW1wbGVzIGluY2x1ZGUgYXBhcnRtZW50IGNvbXBsZXhlcywgcm93IGhvdXNlcyBhbmQgY29tbWVyY2lhbC9pbmR1c3RyaWFsLiBJbXBlcnZpb3VzIHN1cmZhY2VzIGFjY291bnQgZm9yIDgwJSB0byAxMDAlIG9mIHRoZSB0b3RhbCBjb3Zlci4gfAp8IEJhcnJlbiAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IDMxICAgICAgICAgIHwgQmFycmVuIExhbmQgKFJvY2svU2FuZC9DbGF5KSAtIGFyZWFzIG9mIGJlZHJvY2ssIGRlc2VydCBwYXZlbWVudCwgc2NhcnBzLCB0YWx1cywgc2xpZGVzLCB2b2xjYW5pYyBtYXRlcmlhbCwgZ2xhY2lhbCBkZWJyaXMsIHNhbmQgZHVuZXMsIHN0cmlwIG1pbmVzLCBncmF2ZWwgcGl0cyBhbmQgb3RoZXIgYWNjdW11bGF0aW9ucyBvZiBlYXJ0aGVuIG1hdGVyaWFsLiBHZW5lcmFsbHksIHZlZ2V0YXRpb24gYWNjb3VudHMgZm9yIGxlc3MgdGhhbiAxNSUgb2YgdG90YWwgY292ZXIuIHwKfCBGb3Jlc3QgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCA0MSAgICAgICAgICB8IERlY2lkdW91cyBGb3Jlc3QgLSBhcmVhcyBkb21pbmF0ZWQgYnkgdHJlZXMgZ2VuZXJhbGx5IGdyZWF0ZXIgdGhhbiA1IG1ldGVycyB0YWxsLCBhbmQgZ3JlYXRlciB0aGFuIDIwJSBvZiB0b3RhbCB2ZWdldGF0aW9uIGNvdmVyLiBNb3JlIHRoYW4gNzUlIG9mIHRoZSB0cmVlIHNwZWNpZXMgc2hlZCBmb2xpYWdlIHNpbXVsdGFuZW91c2x5IGluIHJlc3BvbnNlIHRvIHNlYXNvbmFsIGNoYW5nZS4gfAp8IDQyICAgICAgICAgIHwgRXZlcmdyZWVuIEZvcmVzdCAtIGFyZWFzIGRvbWluYXRlZCBieSB0cmVlcyBnZW5lcmFsbHkgZ3JlYXRlciB0aGFuIDUgbWV0ZXJzIHRhbGwsIGFuZCBncmVhdGVyIHRoYW4gMjAlIG9mIHRvdGFsIHZlZ2V0YXRpb24gY292ZXIuIE1vcmUgdGhhbiA3NSUgb2YgdGhlIHRyZWUgc3BlY2llcyBtYWludGFpbiB0aGVpciBsZWF2ZXMgYWxsIHllYXIuIENhbm9weSBpcyBuZXZlciB3aXRob3V0IGdyZWVuIGZvbGlhZ2UuIHwKfCA0MyAgICAgICAgICB8IE1peGVkIEZvcmVzdCAtIGFyZWFzIGRvbWluYXRlZCBieSB0cmVlcyBnZW5lcmFsbHkgZ3JlYXRlciB0aGFuIDUgbWV0ZXJzIHRhbGwsIGFuZCBncmVhdGVyIHRoYW4gMjAlIG9mIHRvdGFsIHZlZ2V0YXRpb24gY292ZXIuIE5laXRoZXIgZGVjaWR1b3VzIG5vciBldmVyZ3JlZW4gc3BlY2llcyBhcmUgZ3JlYXRlciB0aGFuIDc1JSBvZiB0b3RhbCB0cmVlIGNvdmVyLiB8CnwgU2hydWJsYW5kICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgNTEgICAgICAgICAgfCBEd2FyZiBTY3J1YiAtIEFsYXNrYSBvbmx5IGFyZWFzIGRvbWluYXRlZCBieSBzaHJ1YnMgbGVzcyB0aGFuIDIwIGNlbnRpbWV0ZXJzIHRhbGwgd2l0aCBzaHJ1YiBjYW5vcHkgdHlwaWNhbGx5IGdyZWF0ZXIgdGhhbiAyMCUgb2YgdG90YWwgdmVnZXRhdGlvbi4gVGhpcyB0eXBlIGlzIG9mdGVuIGNvLWFzc29jaWF0ZWQgd2l0aCBncmFzc2VzLCBzZWRnZXMsIGhlcmJzLCBhbmQgbm9uLXZhc2N1bGFyIHZlZ2V0YXRpb24uIHwKfCA1MiAgICAgICAgICB8IFNocnViL1NjcnViIC0gYXJlYXMgZG9taW5hdGVkIGJ5IHNocnViczsgbGVzcyB0aGFuIDUgbWV0ZXJzIHRhbGwgd2l0aCBzaHJ1YiBjYW5vcHkgdHlwaWNhbGx5IGdyZWF0ZXIgdGhhbiAyMCUgb2YgdG90YWwgdmVnZXRhdGlvbi4gVGhpcyBjbGFzcyBpbmNsdWRlcyB0cnVlIHNocnVicywgeW91bmcgdHJlZXMgaW4gYW4gZWFybHkgc3VjY2Vzc2lvbmFsIHN0YWdlIG9yIHRyZWVzIHN0dW50ZWQgZnJvbSBlbnZpcm9ubWVudGFsIGNvbmRpdGlvbnMuIHwKfCBIZXJiYWNlb3VzICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCA3MSAgICAgICAgICB8IEdyYXNzbGFuZC9IZXJiYWNlb3VzIC0gYXJlYXMgZG9taW5hdGVkIGJ5IGdyYW1hbm9pZCBvciBoZXJiYWNlb3VzIHZlZ2V0YXRpb24sIGdlbmVyYWxseSBncmVhdGVyIHRoYW4gODAlIG9mIHRvdGFsIHZlZ2V0YXRpb24uIFRoZXNlIGFyZWFzIGFyZSBub3Qgc3ViamVjdCB0byBpbnRlbnNpdmUgbWFuYWdlbWVudCBzdWNoIGFzIHRpbGxpbmcsIGJ1dCBjYW4gYmUgdXRpbGl6ZWQgZm9yIGdyYXppbmcuIHwKfCA3MiAgICAgICAgICB8IFNlZGdlL0hlcmJhY2VvdXMgLSBBbGFza2Egb25seSBhcmVhcyBkb21pbmF0ZWQgYnkgc2VkZ2VzIGFuZCBmb3JicywgZ2VuZXJhbGx5IGdyZWF0ZXIgdGhhbiA4MCUgb2YgdG90YWwgdmVnZXRhdGlvbi4gVGhpcyB0eXBlIGNhbiBvY2N1ciB3aXRoIHNpZ25pZmljYW50IG90aGVyIGdyYXNzZXMgb3Igb3RoZXIgZ3Jhc3MtbGlrZSBwbGFudHMsIGFuZCBpbmNsdWRlcyBzZWRnZSB0dW5kcmEsIGFuZCBzZWRnZSB0dXNzb2NrIHR1bmRyYS4gfAp8IDczICAgICAgICAgIHwgTGljaGVucyAtIEFsYXNrYSBvbmx5IGFyZWFzIGRvbWluYXRlZCBieSBmcnV0aWNvc2Ugb3IgZm9saW9zZSBsaWNoZW5zIGdlbmVyYWxseSBncmVhdGVyIHRoYW4gODAlIG9mIHRvdGFsIHZlZ2V0YXRpb24uIHwKfCA3NCAgICAgICAgICB8IE1vc3MgLSBBbGFza2Egb25seSBhcmVhcyBkb21pbmF0ZWQgYnkgbW9zc2VzLCBnZW5lcmFsbHkgZ3JlYXRlciB0aGFuIDgwJSBvZiB0b3RhbCB2ZWdldGF0aW9uLiB8CnwgUGxhbnRlZC9DdWx0aXZhdGVkIHwgICAgICAgICAgICAgICAgICAgICAgfAp8IDgxICAgICAgICAgIHwgUGFzdHVyZS9IYXkgLSBhcmVhcyBvZiBncmFzc2VzLCBsZWd1bWVzLCBvciBncmFzcy1sZWd1bWUgbWl4dHVyZXMgcGxhbnRlZCBmb3IgbGl2ZXN0b2NrIGdyYXppbmcgb3IgdGhlIHByb2R1Y3Rpb24gb2Ygc2VlZCBvciBoYXkgY3JvcHMsIHR5cGljYWxseSBvbiBhIHBlcmVubmlhbCBjeWNsZS4gUGFzdHVyZS9oYXkgdmVnZXRhdGlvbiBhY2NvdW50cyBmb3IgZ3JlYXRlciB0aGFuIDIwJSBvZiB0b3RhbCB2ZWdldGF0aW9uLiB8CnwgODIgICAgICAgICAgfCBDdWx0aXZhdGVkIENyb3BzIC0gYXJlYXMgdXNlZCBmb3IgdGhlIHByb2R1Y3Rpb24gb2YgYW5udWFsIGNyb3BzLCBzdWNoIGFzIGNvcm4sIHNveWJlYW5zLCB2ZWdldGFibGVzLCB0b2JhY2NvLCBhbmQgY290dG9uLCBhbmQgYWxzbyBwZXJlbm5pYWwgd29vZHkgY3JvcHMgc3VjaCBhcyBvcmNoYXJkcyBhbmQgdmluZXlhcmRzLiBDcm9wIHZlZ2V0YXRpb24gYWNjb3VudHMgZm9yIGdyZWF0ZXIgdGhhbiAyMCUgb2YgdG90YWwgdmVnZXRhdGlvbi4gVGhpcyBjbGFzcyBhbHNvIGluY2x1ZGVzIGFsbCBsYW5kIGJlaW5nIGFjdGl2ZWx5IHRpbGxlZC4gfAp8IFdldGxhbmRzICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IDkwICAgICAgICAgIHwgV29vZHkgV2V0bGFuZHMgLSBhcmVhcyB3aGVyZSBmb3Jlc3Qgb3Igc2hydWJsYW5kIHZlZ2V0YXRpb24gYWNjb3VudHMgZm9yIGdyZWF0ZXIgdGhhbiAyMCUgb2YgdmVnZXRhdGl2ZSBjb3ZlciBhbmQgdGhlIHNvaWwgb3Igc3Vic3RyYXRlIGlzIHBlcmlvZGljYWxseSBzYXR1cmF0ZWQgd2l0aCBvciBjb3ZlcmVkIHdpdGggd2F0ZXIuIHwKfCA5NSAgICAgICAgICB8IEVtZXJnZW50IEhlcmJhY2VvdXMgV2V0bGFuZHMgLSBBcmVhcyB3aGVyZSBwZXJlbm5pYWwgaGVyYmFjZW91cyB2ZWdldGF0aW9uIGFjY291bnRzIGZvciBncmVhdGVyIHRoYW4gODAlIG9mIHZlZ2V0YXRpdmUgY292ZXIgYW5kIHRoZSBzb2lsIG9yIHN1YnN0cmF0ZSBpcyBwZXJpb2RpY2FsbHkgc2F0dXJhdGVkIHdpdGggb3IgY292ZXJlZCB3aXRoIHdhdGVyLiB8CgoKIApTZXZlcmFsIHJhc3RlciBsYXllcnMgaGF2ZSBiZWVuIHByb3ZpZGVkIGZvciB0aGlzIGFuYWx5c2lzOiAKCi0gV2UgcmVhZCBpbiBgQ3ZpbGxlTVNBYCAtIHRoaXMgaXMgdGhlIGV4dGVudCBvZiB0aGUgc3R1ZHkgYXJlYSAKCi0gYGxjX2NoYW5nZWAgaXMgYSByYXN0ZXIgb2YgbGFuZCBjb3ZlciBjaGFuZ2UgLSB3aGVyZSB0aGVyZSB3ZXJlIGNvbnZlcnNpb25zIGJldHdlZW4gb25lIGxhbmQgY292ZXIgYW5kIGFub3RoZXIgb24gdGhlIHRpbWUgZnJhbWUgMjAwMS0yMDE5LiBXZSBwbG90IHRoZSByYXN0ZXIgdXNpbmcgYGdncGxvdGAgYW5kIHRoZSBgcmFzdGAgZnVuY3Rpb24gc3BlY2lmaWVkIGFib3ZlLgoKTm90ZSB0aGF0IGBsY19jaGFuZ2VgIGlzIHByb2plY3RlZCBhcyBgTkFEIDE5ODMgU3RhdGVQbGFuZSBWaXJnaW5pYSBTb3V0aCBGSVVTYCBhbmQgaXMgc3BhdGlhbGx5IHJlZmVycmVkIHVzaW5nIGBFUFNHOjIyODRgLiBUaGUgb3JpZ2luYWwgbGFuZCBjb3ZlciByYXN0ZXIgaXMgYXQgYSAzMCBtZXRlciBieSAzMCBtZXRlciByZXNvbHV0aW9uLiBUaGUgcmFzdGVycyBwcm92aWRlZCBhcmUgdWx0aW1hdGVseSByZXNhbXBsZWQgdXAgdG8gNDAwMCBmZWV0IGJ5IDQwMDAgZmVldC4gCgoKYGBge3IgbG9hZF9kYXRhLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgcmVzdWx0cyA9ICJoaWRlIn0KQ3ZpbGxlTVNBIDwtCiAgIyBIaWdoLVJlc29sdXRpb24gVmVyc2lvbjoKICBzdF9yZWFkKCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vWWluZ3RvbmctWi9zcHJhd2wtZm9yZWNhc3RpbmcvYjUyOGQ3MjVlYWVhNWRkNzc4YzQxZmE2NDRiNWU2ZjcxZDYyOTFiMi9kYXRhL0N2aWxsZV9IZXguZ2VvanNvbiIpICU+JQogICMgTWVkaXVtLVJlc29sdXRpb24gVmVyc2lvbjoKICAjIHN0X3JlYWQoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9ZaW5ndG9uZy1aL3NwcmF3bC1mb3JlY2FzdGluZy9hMzY0OWM1ZmYwNjU4NTUwMGMyMTliMzk5MDk5MTA2NWFkM2MwYTZkL2RhdGEvQ3ZpbGxlX1Rlc3NlbGF0ZWRfQ29hcnNlLmdlb2pzb24iKSAlPiUKICAjIExvdy1SZXNvbHV0aW9uIFZlcnNpb246CiAgIyBzdF9yZWFkKCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vWWluZ3RvbmctWi9zcHJhd2wtZm9yZWNhc3RpbmcvYTM2NDljNWZmMDY1ODU1MDBjMjE5YjM5OTA5OTEwNjVhZDNjMGE2ZC9kYXRhL0N2aWxsZV9UZXNzZWxhdGVkX0NvYXJzZXIuZ2VvanNvbiIpICU+JQogIHN0X3RyYW5zZm9ybSgnRVBTRzoyMjg0JykKCmxjX2NoYW5nZSA9IHJhc3RlcigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL1lpbmd0b25nLVovc3ByYXdsLWZvcmVjYXN0aW5nL2I1MjhkNzI1ZWFlYTVkZDc3OGM0MWZhNjQ0YjVlNmY3MWQ2MjkxYjIvZGF0YS9MQ19DaGFuZ2VfMjAwMV8yMDE5LnRpZiIpCiAgCmxjX2NoYW5nZSA8LSBwcm9qZWN0UmFzdGVyKGxjX2NoYW5nZSwgY3JzID0gQ1JTKCIraW5pdD1lcHNnOjIyODQiKSkKCmBgYAoKV2Ugbm93IHBsb3QgTGFuZCBDb3ZlciBDaGFuZ2Ugd2l0aGluIG91ciBNU0EuCgpgYGB7ciBwbG90X21zYSwgd2FybmluZz0gRkFMU0UsIG1lc3NhZ2U9IEZBTFNFfQpnZ3Bsb3QoKSArCiAgZ2VvbV9yYXN0ZXIoZGF0YT1yYXN0KGxjX2NoYW5nZSkgJT4lIG5hLm9taXQgJT4lIGZpbHRlcih2YWx1ZSA+IDApLCAKICAgICAgICAgICAgICBhZXMoeCx5LGZpbGw9YXMuZmFjdG9yKHZhbHVlKSkpICsKICBzY2FsZV9maWxsX3ZpcmlkaXMoZGlyZWN0aW9uID0gLTEsIGRpc2NyZXRlPVRSVUUsIG5hbWUgPSJMYW5kIENvdmVyXG5DaGFuZ2UiKSArCiAgbGFicyh0aXRsZSA9ICJMYW5kIENvdmVyIENoYW5nZSwgMjAwMC0yMDEwIikgKwogIG1hcFRoZW1lICsKICB0aGVtZShsZWdlbmQuZGlyZWN0aW9uPSJob3Jpem9udGFsIikKYGBgCgpOZXh0LCB3ZSByZWNsYXNzaWZ5IHRoZSByYXN0ZXIgc3VjaCB0aGF0IGFsbCB0aGUgZGV2ZWxvcGVkIGdyaWQgY2VsbCB2YWx1ZXMgcmVjZWl2ZSBhIHZhbHVlIG9mIDEgYW5kIGFsbCBvdGhlciB2YWx1ZXMgcmVjZWl2ZSBhIHZhbHVlIG9mIDAuIFRoaXMgaXMgZG9uZSB1c2luZyBhIHJlY2xhc3NpZnkgbWF0cml4LiBUaGUgbWF0cml4IHJlYWRzIHJvdyBieSByb3cuIFJvdyAxIHNheXMgYW55IGdyaWQgY2VsbCByYW5naW5nIGZyb20gMCB0byAxMiB0YWtlcyBhIHZhbHVlIG9mIDA7IDEzIG9yIGdyZWF0ZXIgdGhyb3VnaCAyNCwgYSB2YWx1ZSBvZiAxOyBhbmQgYWxsIG90aGVyIHZhbHVlcyB0YWtlIDAuCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CnJlY2xhc3NNYXRyaXggPC0gIG1hdHJpeChjKAogICAgMCwyLDAsCiAgICAyLDMsMSwKICAgIDMsSW5mLDApLAogIG5jb2w9MywgYnlyb3c9VCkKCnJlY2xhc3NNYXRyaXgKYGBgCgpOb3cgYHJlY2xhc3NpZnlgIGFuZCBjb252ZXJ0IGFsbCAw4oCZcyB0byBgTkFgLiBXZSBhcHBseSBhIG5hbWUgdG8gdGhlIHJhc3RlciB3aXRoIGBuYW1lc2AuIFRoaXMgaXMgZG9uZSB0byBtYWtlIGl0IGZhc3RlciB0byBqb2luIHJhc3RlciB0byB0aGUgZmlzaG5ldCBiZWxvdy4gCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmxjX2NoYW5nZTIgPC0gCiAgcmVjbGFzc2lmeShsY19jaGFuZ2UscmVjbGFzc01hdHJpeCkKCmxjX2NoYW5nZTJbbGNfY2hhbmdlMiA8IDFdIDwtIE5BCgpuYW1lcyhsY19jaGFuZ2UyKSA8LSAibGNfY2hhbmdlIgoKZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YT1DdmlsbGVNU0EsIGZpbGwgPSAiZ3JleSIsIGNvbG9yID0gInRyYW5zcGFyZW50IikrCiAgZ2VvbV9yYXN0ZXIoZGF0YT1yYXN0KGxjX2NoYW5nZTIpICU+JSBuYS5vbWl0LCAKICAgICAgICAgICAgICBhZXMoeCx5LGZpbGw9YXMuZmFjdG9yKHZhbHVlKSkpICsKICBzY2FsZV9maWxsX3ZpcmlkaXMoZGlzY3JldGU9VFJVRSwgbmFtZSA9IkxhbmQgQ292ZXJcbkNoYW5nZSIpICsgCiAgbGFicyh0aXRsZT0iRGV2ZWxvcG1lbnQgTGFuZCBVc2UgQ2hhbmdlIikgKwogIG1hcFRoZW1lCmBgYAoKVGhlIHZlY3RvciBmaXNobmV0IGlzIHRoZW4gcGxvdHRlZC4KCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2U9IEZBTFNFfQpnZ3Bsb3QoKSArCiAgZ2VvbV9zZihkYXRhPUN2aWxsZU1TQSwgZmlsbCA9ICJncmV5IiwgY29sb3I9ICJibGFjayIpICsKICBsYWJzKHRpdGxlPSJIZXhhZ29uYWwgRmlzaG5ldCwgMjUwayBzcSBmdCBjZWxscyIpICsKICBtYXBUaGVtZQpgYGAKCgpUaGUgY29kZSBiZWxvdyBjb252ZXJ0cyB0aGUgcmFzdGVyIHRvIGFuIGBzZmAgcG9pbnQgbGF5ZXIgYW5kIHRoZW4gam9pbnMgdGhlIHBvaW50cyB0byB0aGUgZmlzaG5ldCB3aXRoIGBhZ2dyZWdhdGVgLiBGaW5hbGx5LCB0aGUgZmlzaG5ldCB2YXJpYWJsZSBgbGNfY2hhbmdlYCBpcyBjcmVhdGVkIHRoYXQgaXMgYDFgIHdoZXJlIG5ldyBkZXZlbG9wbWVudCBoYXMgb2NjdXJyZWQgYW5kIGAwYCB3aGVyZSBpdCBoYXMgbm90LiBUaGlzIGlzIG91ciBkZXBlbmRlbnQgdmFyaWFibGUgYW5kIGVuY29kZWQgYXMgYSBmYWN0b3IuCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmNoYW5nZVBvaW50cyA8LQogIHJhc3RlclRvUG9pbnRzKGxjX2NoYW5nZTIpICU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBzdF9hc19zZihjb29yZHMgPSBjKCJ4IiwgInkiKSwgY3JzID0gc3RfY3JzKEN2aWxsZU1TQSkpCgpmaXNobmV0IDwtIAogIGFnZ3JlZ2F0ZShjaGFuZ2VQb2ludHMsIEN2aWxsZU1TQSwgc3VtKSAlPiUKICBtdXRhdGUobGNfY2hhbmdlID0gaWZlbHNlKGlzLm5hKGxjX2NoYW5nZSksMCwxKSwKICAgICAgICAgbGNfY2hhbmdlID0gYXMuZmFjdG9yKGxjX2NoYW5nZSkpCiMgUGxvdAojIFRvIHNwZWVkIHVwIHRoZSBtYXBwaW5nIHByb2Nlc3MsIGZpc2huZXQgcG9seWdvbnMgYXJlIGNvbnZlcnRlZCB0byBjZW50cm9pZCBwb2ludHMgdXNpbmcgdGhlIGB4eUNgIGZ1bmN0aW9uIGRlZmluZWQgZWFybGllciAgCmdncGxvdCgpICsKICBnZW9tX3NmKGRhdGE9ZmlzaG5ldCwgYWVzKGZpbGw9bGNfY2hhbmdlKSwgY29sb3I9TkEpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwgbGFiZWxzPWMoIk5vIENoYW5nZSIsIk5ldyBEZXZlbG9wbWVudCIpLCBuYW1lID0gIiIpICsKICBsYWJzKHRpdGxlID0gIkxhbmQgQ292ZXIgRGV2ZWxvcG1lbnQgQ2hhbmdlIiwgc3VidGl0bGUgPSAiQXMgZmlzaG5ldCBjZW50cm9pZHMiKSArCiAgbWFwVGhlbWUKYGBgCgoKIyMgMi4zLiBMYW5kIENvdmVyIGluIDIwMDEKCkl0IGlzIHJlYXNvbmFibGUgdG8gaHlwb3RoZXNpemUgdGhhdCB0aGUgcHJvcGVuc2l0eSBvZiBuZXcgZGV2ZWxvcG1lbnQgaXMgYSBmdW5jdGlvbiBvZiBleGlzdGluZyBsYW5kIGNvdmVyIGNhdGVnb3JpZXMuIEluIHRoaXMgc2VjdGlvbiB3ZSBpZGVudGlmeSB0aGVzZSBvdGhlciBsYW5kIGNvdmVyIGNhdGVnb3JpZXMgZnJvbSAyMDAxIGFuZCBpbnRlZ3JhdGUgZWFjaCB3aXRoIHRoZSBmaXNobmV0LgoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpsY18yMDAxIDwtIHJhc3RlcigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL1lpbmd0b25nLVovc3ByYXdsLWZvcmVjYXN0aW5nL2I1MjhkNzI1ZWFlYTVkZDc3OGM0MWZhNjQ0YjVlNmY3MWQ2MjkxYjIvZGF0YS9DdmlsbGVfTENfMjAwMS50aWYiKQoKZ2dwbG90KCkgKwogIGdlb21fcmFzdGVyKGRhdGE9cmFzdChsY18yMDAxKSAlPiUgbmEub21pdCAlPiUgZmlsdGVyKHZhbHVlID4gMCksIAogICAgICAgICAgICAgIGFlcyh4LHksZmlsbD1hcy5mYWN0b3IodmFsdWUpKSkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpcyhkaXNjcmV0ZT1UUlVFLCBuYW1lID0iIikgKwogIGxhYnModGl0bGUgPSAiTGFuZCBDb3ZlciwgMjAwMSIpICsKICBtYXBUaGVtZSArCiAgdGhlbWUobGVnZW5kLmRpcmVjdGlvbj0iaG9yaXpvbnRhbCIpCmBgYAoKClRoZSB0YWJsZSBiZWxvdyBzaG93cyB0aGUgYXBwcm9hY2ggdGFrZW4gdG8gcmVjb2RlZCBleGlzdGluZyBsYW5kIGNvdmVyIGNvZGVzIGludG8gdGhlIGNhdGVnb3JpZXMgdXNlZCBpbiBvdXIgYW5hbHlzaXMuIEluIHRoZSBjb2RlIGJsb2NrIGJlbG93IG5ldyByYXN0ZXJzIGFyZSBnZW5lcmF0ZWQgYW5kIGBuYW1lc2AgYXJlIGFwcGxpZWQuIE5hbWluZyBlbnN1cmVzIHRoYXQgd2hlbiB0aGUgcmFzdGVyIGlzIGludGVncmF0ZWQgd2l0aCB0aGUgZmlzaG5ldCwgdGhlIGNvbHVtbiByZWZsZWN0cyB0aGUgYXBwcm9wcmlhdGUgcmFzdGVyLgoKfCBPbGRfQ2xhc3NpZmljYXRpb24gICAgICAgICAgICAgfCBOZXdfQ2xhc3NpZmljYXRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18CnwgT3BlbiBTcGFjZSBhcyB3ZWxsIGFzIExvdywgTWVkaXVtIGFuZCBIaWdoIEludGVuc2l0eSBEZXZlbG9wbWVudCB8IERldmVsb3BlZCB8CnwgRGVjaWR1b3VzLCBFdmVyZ3JlZW4sIGFuZCBNaXhlZCBGb3Jlc3QgfCAgRm9yZXN0IHwKfCBQYXN0dXJlL0hheSBhbmQgQ3VsdGl2YXRlZCBDcm9wcyB8IEZhcm0gfAp8IFdvb2R5IGFuZCBFbWVyZ2VudCBIZXJiYWNlb3VzIFdldGxhbmRzIHwgV29vZGxhbmRzIHwKfCBCYXJyZW4gTGFuZCwgRHdhcmYgU2NydWIsIGFuZCBHcmFzc2xhbmQvSGVyYmFjZW91cyB8IE90aGVyIFVuZGV2ZWxvcGVkIHwKfCBXYXRlciB8IFdhdGVyIHwKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KZGV2ZWxvcGVkIDwtIGxjXzIwMDEgPT0gMjEgfCBsY18yMDAxID09IDIyIHwgbGNfMjAwMSA9PSAyMyB8IGxjXzIwMDEgPT0gMjQKZm9yZXN0IDwtIGxjXzIwMDEgPT0gNDEgfCBsY18yMDAxID09IDQyIHwgbGNfMjAwMSA9PSA0MyAKZmFybSA8LSBsY18yMDAxID09IDgxIHwgbGNfMjAwMSA9PSA4MiAKd2V0bGFuZHMgPC0gbGNfMjAwMSA9PSA5MCB8IGxjXzIwMDEgPT0gOTUgCm90aGVyVW5kZXZlbG9wZWQgPC0gbGNfMjAwMSA9PSA1MiB8IGxjXzIwMDEgPT0gNzEgfCBsY18yMDAxID09IDMxIAp3YXRlciA8LSBsY18yMDAxID09IDExCgpuYW1lcyhkZXZlbG9wZWQpIDwtICJkZXZlbG9wZWQiCm5hbWVzKGZvcmVzdCkgPC0gImZvcmVzdCIKbmFtZXMoZmFybSkgPC0gImZhcm0iCm5hbWVzKHdldGxhbmRzKSA8LSAid2V0bGFuZHMiCm5hbWVzKG90aGVyVW5kZXZlbG9wZWQpIDwtICJvdGhlclVuZGV2ZWxvcGVkIgpuYW1lcyh3YXRlcikgPC0gIndhdGVyIgpgYGAKCk5leHQsIGVhY2ggcmFzdGVyIGlzIGFnZ3JlZ2F0ZWQgdG8gdGhlIGZpc2huZXQgYnkgd2F5IG9mIGEgZnVuY3Rpb24gY2FsbGVkIGBhZ2dyZWdhdGVSYXN0ZXJgLiBIZXJlLCB0aGUgcHJvY2VzcyB1c2VkIGFib3ZlIHRvIFRvIGRvIHRoaXMsIGEgZnVuY3Rpb24gaXMgY3JlYXRlZCBiZWxvdyB0aGF0IGxvb3BzIHRocm91Z2ggYSBsaXN0IG9mIHJhc3RlcnMsIGNvbnZlcnRzIHRoZSBfaXRoXyByYXN0ZXIgdG8gcG9pbnRzLCBmaWx0ZXJzIG9ubHkgcG9pbnRzIHRoYXQgaGF2ZSB2YWx1ZSBvZiBgMWAgKGllLiBpcyB0aGUgX2l0aF8gbGFuZCBjb3ZlciB0eXBlKSwgYW5kIHRoZW4gYWdncmVnYXRlcyB0byB0aGUgZmlzaG5ldC4KCkhlcmUgaXMgdGhlIGZ1bmN0aW9uLgoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQphZ2dyZWdhdGVSYXN0ZXIgPC0gZnVuY3Rpb24oaW5wdXRSYXN0ZXJMaXN0LCB0aGVGaXNobmV0KSB7CiAgI2NyZWF0ZSBhbiBlbXB0eSBmaXNobmV0IHdpdGggdGhlIHNhbWUgZGltZW5zaW9ucyBhcyB0aGUgaW5wdXQgZmlzaG5ldAogIHRoZXNlRmlzaG5ldHMgPC0gdGhlRmlzaG5ldCAlPiUgZHBseXI6OnNlbGVjdCgpCiAgI2ZvciBlYWNoIHJhc3RlciBpbiB0aGUgcmFzdGVyIGxpc3QKICBmb3IgKGkgaW4gaW5wdXRSYXN0ZXJMaXN0KSB7CiAgI2NyZWF0ZSBhIHZhcmlhYmxlIG5hbWUgY29ycmVzcG9uZGluZyB0byB0aGUgaXRoIHJhc3RlcgogIHZhck5hbWUgPC0gbmFtZXMoaSkKICAjY29udmVydCByYXN0ZXIgdG8gcG9pbnRzIGFzIGFuIHNmCiAgICB0aGVzZVBvaW50cyA8LQogICAgICByYXN0ZXJUb1BvaW50cyhpKSAlPiUKICAgICAgYXMuZGF0YS5mcmFtZSgpICU+JQogICAgICBzdF9hc19zZihjb29yZHMgPSBjKCJ4IiwgInkiKSwgY3JzID0gc3RfY3JzKHRoZUZpc2huZXQpKSAlPiUKICAgICAgZmlsdGVyKC5bWzFdXSA9PSAxKQogICNhZ2dyZWdhdGUgdG8gdGhlIGZpc2huZXQKICAgIHRoaXNGaXNobmV0IDwtCiAgICAgIGFnZ3JlZ2F0ZSh0aGVzZVBvaW50cywgdGhlRmlzaG5ldCwgbGVuZ3RoKSAlPiUKICAgICAgbXV0YXRlKCEhdmFyTmFtZSA6PSBpZmVsc2UoaXMubmEoLltbMV1dKSwwLDEpKQogICNhZGQgdG8gdGhlIGxhcmdlciBmaXNobmV0CiAgICB0aGVzZUZpc2huZXRzIDwtIGNiaW5kKHRoZXNlRmlzaG5ldHMsdGhpc0Zpc2huZXQpCiAgfQogICNvdXRwdXQgYWxsIGFnZ3JlZ2F0ZXMgYXMgb25lIGxhcmdlIGZpc2huZXQKICAgcmV0dXJuKHRoZXNlRmlzaG5ldHMpCiAgfQpgYGAKClRoZSBgdGhlUmFzdGVyTGlzdGAgb2YgbGFuZCBjb3ZlciB0eXBlcyBpbiAyMDAxIGlzIGNyZWF0ZWQgYW5kIHRoZW4gZmVkIGludG8gYGFnZ3JlZ2F0ZVJhc3RlcmAuIFRoZSByZXN1bHQgaXMgY29udmVydGVkIHRvIGxvbmcgZm9ybSBncmlkIGNlbGwgY2VudHJvaWRzIGFuZCBwbG90IGFzIHNtYWxsIG11bHRpcGxlIG1hcHMuCgpOb3RlIHRoZSBpbmNsdXNpb24gb2YgYHN0X2Nhc3RgIGhlcmUgd2hpY2ggY29udmVydCBhbGwgZ2VvbWV0cmllcyB0byBgUE9MWUdPTmAuIElmIHdlIGNyZWF0ZSBhIGZyZXF1ZW5jeSB0YWJsZSBvZiBnZW9tZXRyeSB0eXBlcyBpbiBgYWdncmVnYXRlZFJhc3RlcnNgLCB3ZSB3aWxsIG5vdGljZSBzb21lIGFuZCBoYW5kZnVsIG9mIGBNVUxUSVBPTFlHT05TYC4gVHJ5IGB0YWJsZShzdF9nZW9tZXRyeV90eXBlKGFnZ3JlZ2F0ZWRSYXN0ZXJzKWApLiBUaGVzZSByb2d1ZSBtdWx0aXBvbHlnb25zIGJyZWFrIHRoZSBgeHlDYCBmdW5jdGlvbiB3aGljaCBpcyBkZXNpZ25lZCB0byBmaW5kIGdyaWQgY2VsbCBjZW50cm9pZHMuIEFmdGVyIGFsbCwgdGhlcmUgaXMgbm8gb25lIGNlbnRyb2lkIG9mIHNldmVyYWwgY29tYmluZWQgcG9seWdvbnMuIFRodXMgYHN0X2Nhc3RgIGVuc3VyZXMgYWxsIGdlb21ldHJpZXMgYXJlIGp1c3QgYFBPTFlHT05gLgoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQp0aGVSYXN0ZXJMaXN0IDwtIGMoZGV2ZWxvcGVkLGZvcmVzdCxmYXJtLHdldGxhbmRzLG90aGVyVW5kZXZlbG9wZWQsd2F0ZXIpCgphZ2dyZWdhdGVkUmFzdGVycyA8LQogIGFnZ3JlZ2F0ZVJhc3Rlcih0aGVSYXN0ZXJMaXN0LCBDdmlsbGVNU0EpICU+JQogIGRwbHlyOjpzZWxlY3QoZGV2ZWxvcGVkLGZvcmVzdCxmYXJtLHdldGxhbmRzLG90aGVyVW5kZXZlbG9wZWQsd2F0ZXIpICU+JQogIG11dGF0ZV9pZihpcy5udW1lcmljLGFzLmZhY3RvcikKCiMgcmVhc3NpZ24gY2VsbHMgd2l0aCBtb3JlIHRoYW4gb25lIGxjIHZhbHVlIHRvIGp1c3Qgb25lIGJhc2VkIG9uIGhpZXJhcmNoeSBzaG93biBoZXJlCmFnZ3JlZ2F0ZWRSYXN0ZXJzIDwtIGFnZ3JlZ2F0ZWRSYXN0ZXJzICU+JQogICAgbXV0YXRlKGZhcm0gPSBpZmVsc2UoZGV2ZWxvcGVkID09IDAgJiBmYXJtID09IDEsIDEsIDApKSAlPiUKICAgIG11dGF0ZShmb3Jlc3QgPSBpZmVsc2UoZGV2ZWxvcGVkID09IDAgJiBmYXJtID09IDAgJiBmb3Jlc3QgPT0gMSwgMSwgMCkpICU+JQogICAgbXV0YXRlKHdldGxhbmRzID0gaWZlbHNlKGRldmVsb3BlZCA9PSAwICYgZmFybSA9PSAwICYgZm9yZXN0ID09IDAgJiB3ZXRsYW5kcyA9PSAxLCAxLCAwKSkgJT4lCiAgICBtdXRhdGUod2F0ZXIgPSBpZmVsc2UoZGV2ZWxvcGVkID09IDAgJiBmYXJtID09IDAgJiBmb3Jlc3QgPT0gMCAmIHdldGxhbmRzID09IDAgJiB3YXRlciA9PSAxLCAxLCAwKSkgJT4lCiAgICBtdXRhdGUob3RoZXJVbmRldmVsb3BlZCA9IGlmZWxzZShkZXZlbG9wZWQgPT0gMCAmIGZhcm0gPT0gMCAmIGZvcmVzdCA9PSAwICYgd2V0bGFuZHMgPT0gMCAmIHdhdGVyID09IDAgJiBvdGhlclVuZGV2ZWxvcGVkID09IDEsIDEsIDApKQoKYWdncmVnYXRlZFJhc3RlcnMgJT4lCiAgZ2F0aGVyKHZhcix2YWx1ZSxkZXZlbG9wZWQ6d2F0ZXIpICU+JQogIHN0X2Nhc3QoIlBPTFlHT04iKSAlPiUgICAgI2p1c3QgdG8gbWFrZSBzdXJlIG5vIHdlaXJkIGdlb21ldHJpZXMgc2xpcHBlZCBpbgogIG11dGF0ZShYID0geHlDKC4pJHgsCiAgICAgICAgIFkgPSB4eUMoLikkeSkgJT4lCiAgZ2dwbG90KCkgKwogICAgZ2VvbV9zZihkYXRhPUN2aWxsZU1TQSkgKwogICAgZ2VvbV9wb2ludChhZXMoWCxZLCBjb2xvdXI9YXMuZmFjdG9yKHZhbHVlKSksIHNpemUgPTAuMSkgKwogICAgZmFjZXRfd3JhcCh+dmFyKSArCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUyLAogICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiT3RoZXIiLCJMYW5kIENvdmVyIiksCiAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiIikgKwogICAgbGFicyh0aXRsZSA9ICJMYW5kIENvdmVyIFR5cGVzLCAyMDAxIiwKICAgICAgICAgc3VidGl0bGUgPSAiQXMgZmlzaG5ldCBjZW50cm9pZHMiKSArCiAgIG1hcFRoZW1lCmBgYAoKIyMgMi40LiBDZW5zdXMgRGF0YQoKUG9wdWxhdGlvbiBhbmQgcG9wdWxhdGlvbiBjaGFuZ2UgYXJlIGNydWNpYWwgZGVtYW5kLXNpZGUgZmFjdG9ycyBmb3IgcHJlZGljdGluZyBgRGV2ZWxvcG1lbnRfRGVtYW5kYC4gV2Ugb2J0YWluIDIwMDAgYW5kIDIwMjAgY2Vuc3VzIGRhdGEgdGhyb3VnaCB0aGUgYHRpZHljZW5zdXNgIHBhY2thZ2UgdG8gcmVwcmVzZW50IHRoZSBkZW1vZ3JhcGhpYyBmZWF0dXJlcyBmb3IgMjAwMSBhbmQgMjAxOS4gVGhlc2UgZGF0YSBhcmUgZG93bmxvYWRlZCBhdCBhIGJsb2NrIGdyb3VwIGdlb2dyYXBoeSBhbmQgdGh1cywgYW4gYXBwcm9hY2ggaXMgbmVlZGVkIHRvIHJlY29uY2lsZSB0cmFjdHMgYW5kIGZpc2huZXQgZ2VvbWV0cmllcy4gVGhpcyBpcyBhY2NvbXBsaXNoZWQgdXNpbmcgYSB0ZWNobmlxdWUgY2FsbGVkIGFyZWFsIHdlaWdodGVkIGludGVycG9sYXRpb24uCgoKQmVsb3cgd2UgZ2V0IHRoZSBkZW1vZ3JhcGhpYyBkYXRhIGluY2x1ZGluZyBgdG90YWxfcG9wdWxhdGlvbmAsIGB0b3RhbF9ob3VzaW5nX3VuaXRzYCwgYHRvdGFsX3ZhY2FudF9ob3VzZWhvbGRzYCwgYHRvdGFsX3doaXRlYCxgdG90YWxfZ3JhZHVhdGVfZGVncmVlX2hvbGRlcnNgIGZvciBib3RoIDIwMDAgYW5kIDIwMjAuCgpgYGB7ciBsb2FkX2tleV9oaWRlLCB3YXJuaW5nPSBGQUxTRSwgaW5jbHVkZT1GQUxTRX0KIyBTZXQgQVBJIGtleQpjZW5zdXNfYXBpX2tleSgiMjBhZDM4YzZkOWUyNDFiZTQ5YWUzYTFhNmY3NDEyNTQ0OGRiNjc0ZSIsIGluc3RhbGwgPSBUUlVFLCBvdmVyd3JpdGUgPSBUUlVFKQpgYGAKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgcmVzdWx0cyA9ICJoaWRlIn0KCiNmdW5jdGlvbiB0byBnZXQgYW5kIGNsZWFuIDIwMDAgY2Vuc3VzIGRhdGEgKGRlY2VubmlhbCkKY2xlYW5fMjAwMF9jZW5zdXNfZGF0YSA8LSBmdW5jdGlvbigpIHsKICAKICAjdmFyaWJsZXMgdG8gdXNlIGZvciAyMDAwIGRlY2VuaXRhbCB2YXJpYWJsZXMKICB2YXJpYWJsZXMyMDAwQSA8LSBjKCJQMDAxMDAxIiwgIkgwMDEwMDEiLCAiSDAwMzAwMyIsICJQMDAzMDAzIikKICBuYW1lcyh2YXJpYWJsZXMyMDAwQSkgPC0gYygidG90YWxfcG9wdWxhdGlvbiIsICJ0b3RhbF9ob3VzaW5nX3VuaXRzIiwgInRvdGFsX3ZhY2FudF9ob3VzZWhvbGRzIiwgInRvdGFsX3doaXRlIikKICAKICB2YXJpYWJsZXMyMDAwQiA8LSBjKCJQMDUzMDAxIiwiUDAzNjAyMSIsIlAwMzYwNDQiKQogIG5hbWVzKHZhcmlhYmxlczIwMDBCKSA8LSBjKCJtZWRpYW5faG91c2Vob2xkX2luY29tZSIsIk1hbGUgVG90YWwgZ3JhZHVhdGUgZGVncmVlIGhvbGRlcnMiLCAiRmVtYWxlIFRvdGFsIGdyYWR1YXRlIGRlZ3JlZSBob2xkZXJzIikKICAKICAjZnVuY3Rpb24gdG8gZ2V0IDIwMDAgZGVjZW5uaWFsIHZhcmlhYmxlcwogIGdldF9kZWNlbmlhbF92YXJpYWJsZXMgPC0gZnVuY3Rpb24oeWVhciwgc3VtZmlsZSwgdmFyaWFibGVzLCBjb3VudHkpIHsKICAgIGRhdGEgPC0gZ2V0X2RlY2VubmlhbCgKICAgICAgZ2VvZ3JhcGh5ID0gImJsb2NrIGdyb3VwIiwKICAgICAgeWVhciA9IHllYXIsCiAgICAgIHN1bWZpbGUgPSBzdW1maWxlLAogICAgICB2YXJpYWJsZXMgPSB2YXJpYWJsZXMsCiAgICAgIHN0YXRlID0gIlZBIiwKICAgICAgY291bnR5ID0gY291bnR5LAogICAgICBvdXRwdXQgPSAid2lkZSIsCiAgICAgIGdlb21ldHJ5ID0gVFJVRQogICAgKQogICAgCiAgICByZXR1cm4oZGF0YSkKICB9CiAgCiAgIyBnZXQgdmFyaWFibGVzIGZyb20gZGlmZmVyZW50IGRlY2VubmlhbCBmaWxlcyBmb3IgQ1YgYW5kIEFNCiAgZGF0YV9jdjEgPC0gZ2V0X2RlY2VuaWFsX3ZhcmlhYmxlcygyMDAwLCdzZjEnLCB2YXJpYWJsZXMyMDAwQSwgIkNoYXJsb3R0ZXN2aWxsZSIpCiAgZGF0YV9jdjIgPC0gZ2V0X2RlY2VuaWFsX3ZhcmlhYmxlcygyMDAwLCdzZjMnLCB2YXJpYWJsZXMyMDAwQiwgIkNoYXJsb3R0ZXN2aWxsZSIpCiAgZGF0YV9hbTEgPC0gZ2V0X2RlY2VuaWFsX3ZhcmlhYmxlcygyMDAwLCdzZjEnLCB2YXJpYWJsZXMyMDAwQSwgIkFsYmVtYXJsZSIpCiAgZGF0YV9hbTIgPC0gZ2V0X2RlY2VuaWFsX3ZhcmlhYmxlcygyMDAwLCdzZjMnLCB2YXJpYWJsZXMyMDAwQiwgIkFsYmVtYXJsZSIpCiAgCiAgIyBqb2luIHRoZSBkYXRhCiAgY3ZhbV9jZW5zdXMxIDwtIHJiaW5kKGRhdGFfY3YxLCBkYXRhX2FtMSkKICBjdmFtX2NlbnN1czIgPC0gcmJpbmQoZGF0YV9jdjIsIGRhdGFfYW0yKQogIAogIGN2YW1fY2Vuc3VzIDwtIHN0X2pvaW4oY3ZhbV9jZW5zdXMxLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGN2YW1fY2Vuc3VzMlssIGMoIm1lZGlhbl9ob3VzZWhvbGRfaW5jb21lIiwgIk1hbGUgVG90YWwgZ3JhZHVhdGUgZGVncmVlIGhvbGRlcnMiLCAiRmVtYWxlIFRvdGFsIGdyYWR1YXRlIGRlZ3JlZSBob2xkZXJzIildLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGpvaW4gPSBzdF9lcXVhbHMpICAlPiUKICAgIG11dGF0ZShgdG90YWxfZ3JhZHVhdGVfZGVncmVlX2hvbGRlcnNgID0gYE1hbGUgVG90YWwgZ3JhZHVhdGUgZGVncmVlIGhvbGRlcnNgICsgYEZlbWFsZSBUb3RhbCBncmFkdWF0ZSBkZWdyZWUgaG9sZGVyc2ApICU+JQogICAgZHBseXI6OnNlbGVjdCgtYE1hbGUgVG90YWwgZ3JhZHVhdGUgZGVncmVlIGhvbGRlcnNgLCAtYEZlbWFsZSBUb3RhbCBncmFkdWF0ZSBkZWdyZWUgaG9sZGVyc2ApCiAgCiAgcmV0dXJuKGN2YW1fY2Vuc3VzKQp9CgoKCiNmdW5jdGlvbiB0byBnZXQgYW5kIGNsZWFuIDIwMjAgY2Vuc3VzIGRhdGEgKGFjczUpCmNsZWFuXzIwMjBfY2Vuc3VzX2RhdGEgPC0gZnVuY3Rpb24oKSB7CiAgCiAgI3ZhcmlibGVzIHRvIHVzZSBmb3IgMjAyMCBkZWNlbml0YWwgdmFyaWFibGVzCiAgdmFyaWFibGVzMjAyMEEgPC0gYygiQjAxMDAzXzAwMUUiLCAiQjI1MDAxXzAwMUUiLCAiQjI1MDAyXzAwM0UiLCAiQjAyMDAxXzAwMkUiKQogIG5hbWVzKHZhcmlhYmxlczIwMjBBKSA8LSBjKCJ0b3RhbF9wb3B1bGF0aW9uIiwgInRvdGFsX2hvdXNpbmdfdW5pdHMiLCAidG90YWxfdmFjYW50X2hvdXNlaG9sZHMiLCAidG90YWxfd2hpdGUiKQogIAogIHZhcmlhYmxlczIwMjBCIDwtIGMoIkIxOTA0OV8wMDFFIiwiQjE0MDAyXzAyMkUiLCJCMTQwMDJfMDQ2RSIpCiAgbmFtZXModmFyaWFibGVzMjAyMEIpIDwtIGMoIm1lZGlhbl9ob3VzZWhvbGRfaW5jb21lIiwiTWFsZSBUb3RhbCBncmFkdWF0ZSBkZWdyZWUgaG9sZGVycyIsICJGZW1hbGUgVG90YWwgZ3JhZHVhdGUgZGVncmVlIGhvbGRlcnMiKQogIAogICNmdW5jdGlvbiB0byBnZXQgMjAyMCBkZWNlbml0YWwgdmFyaWFibGVzCiAgZ2V0X2Fjc192YXJpYWJsZXMgPC0gZnVuY3Rpb24oeWVhciwgdmFyaWFibGVzLCBjb3VudHkpIHsKICAgIGRhdGEgPC0gZ2V0X2FjcygKICAgICAgZ2VvZ3JhcGh5ID0gImJsb2NrIGdyb3VwIiwKICAgICAgc3VydmV5ID0gImFjczUiLAogICAgICB5ZWFyID0geWVhciwKICAgICAgdmFyaWFibGVzID0gdmFyaWFibGVzLAogICAgICBzdGF0ZSA9ICJWQSIsCiAgICAgIGNvdW50eSA9IGNvdW50eSwKICAgICAgb3V0cHV0ID0gIndpZGUiLAogICAgICBnZW9tZXRyeSA9IFRSVUUKICAgICkKICAgIAogICAgcmV0dXJuKGRhdGEpCiAgfQogIAogICMgZ2V0IHZhcmlhYmxlcyBmcm9tIGRpZmZlcmVudCBhY3MgZmlsZXMgZm9yIENWIGFuZCBBTQogIGRhdGFfY3YxIDwtIGdldF9hY3NfdmFyaWFibGVzKDIwMjAsIHZhcmlhYmxlczIwMjBBLCAiQ2hhcmxvdHRlc3ZpbGxlIikgJT4lIGRwbHlyOjpzZWxlY3QoIWVuZHNfd2l0aCgiTSIpKQogIGRhdGFfY3YyIDwtIGdldF9hY3NfdmFyaWFibGVzKDIwMjAsIHZhcmlhYmxlczIwMjBCLCAiQ2hhcmxvdHRlc3ZpbGxlIikgJT4lIGRwbHlyOjpzZWxlY3QoIWVuZHNfd2l0aCgiTSIpKQogIGRhdGFfYW0xIDwtIGdldF9hY3NfdmFyaWFibGVzKDIwMjAsIHZhcmlhYmxlczIwMjBBLCAiQWxiZW1hcmxlIikgJT4lIGRwbHlyOjpzZWxlY3QoIWVuZHNfd2l0aCgiTSIpKQogIGRhdGFfYW0yIDwtIGdldF9hY3NfdmFyaWFibGVzKDIwMjAsIHZhcmlhYmxlczIwMjBCLCAiQWxiZW1hcmxlIikgJT4lIGRwbHlyOjpzZWxlY3QoIWVuZHNfd2l0aCgiTSIpKQogIAogICMgam9pbiB0aGUgZGF0YQogIGN2YW1fY2Vuc3VzMSA8LSByYmluZChkYXRhX2N2MSwgZGF0YV9hbTEpCiAgY3ZhbV9jZW5zdXMyIDwtIHJiaW5kKGRhdGFfY3YyLCBkYXRhX2FtMikKICAKICBjdmFtX2NlbnN1cyA8LSBzdF9qb2luKGN2YW1fY2Vuc3VzMSwgCiAgICAgICAgICAgICAgICAgICAgICAgICBjdmFtX2NlbnN1czJbLCBjKCJtZWRpYW5faG91c2Vob2xkX2luY29tZSIsICJNYWxlIFRvdGFsIGdyYWR1YXRlIGRlZ3JlZSBob2xkZXJzIiwgIkZlbWFsZSBUb3RhbCBncmFkdWF0ZSBkZWdyZWUgaG9sZGVycyIpXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICBqb2luID0gc3RfZXF1YWxzKSAgJT4lCiAgICBtdXRhdGUoYHRvdGFsX2dyYWR1YXRlX2RlZ3JlZV9ob2xkZXJzYCA9IGBNYWxlIFRvdGFsIGdyYWR1YXRlIGRlZ3JlZSBob2xkZXJzYCArIGBGZW1hbGUgVG90YWwgZ3JhZHVhdGUgZGVncmVlIGhvbGRlcnNgKSAlPiUKICAgIGRwbHlyOjpzZWxlY3QoLWBNYWxlIFRvdGFsIGdyYWR1YXRlIGRlZ3JlZSBob2xkZXJzYCwgLWBGZW1hbGUgVG90YWwgZ3JhZHVhdGUgZGVncmVlIGhvbGRlcnNgKQogIAogIHJldHVybihjdmFtX2NlbnN1cykKfQoKCiMgcnVuIHRoZSBmdW5jdGlvbnMgCmN2YW1fMjAwMF9jZW5zdXMgPC0gY2xlYW5fMjAwMF9jZW5zdXNfZGF0YSgpICMgMjAwMCBjZW5zdXMgZGF0YQpjdmFtXzIwMjBfY2Vuc3VzIDwtIGNsZWFuXzIwMjBfY2Vuc3VzX2RhdGEoKSAjIDIwMjAgY2Vuc3VzIGRhdGEKYGBgCgpBZGRpdGlvbmFsIENlbnN1cyBEYXRhIEZvcm1hdHRpbmcgZm9yIDIwMjAuCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KY3ZhbV8yMDIwX2NlbnN1cyA8LSBjdmFtXzIwMjBfY2Vuc3VzICU+JSAKICBtdXRhdGUodG90YWxfcG9wdWxhdGlvbjIwMjAgPSB0b3RhbF9wb3B1bGF0aW9uKSAlPiUKICBtdXRhdGUodG90YWxfaG91c2luZ191bml0czIwMjAgPSB0b3RhbF9ob3VzaW5nX3VuaXRzKSAlPiUKICBtdXRhdGUodG90YWxfdmFjYW50X2hvdXNlaG9sZHMyMDIwID0gdG90YWxfdmFjYW50X2hvdXNlaG9sZHMpICU+JQogIG11dGF0ZSh0b3RhbF93aGl0ZTIwMjAgPSB0b3RhbF93aGl0ZSkgJT4lCiAgbXV0YXRlKG1lZGlhbl9ob3VzZWhvbGRfaW5jb21lMjAyMCA9IG1lZGlhbl9ob3VzZWhvbGRfaW5jb21lKSAlPiUKICBtdXRhdGUodG90YWxfZ3JhZHVhdGVfZGVncmVlX2hvbGRlcnMyMDIwID0gdG90YWxfZ3JhZHVhdGVfZGVncmVlX2hvbGRlcnMpICU+JQogIGRwbHlyOjpzZWxlY3QoLWB0b3RhbF9wb3B1bGF0aW9uYCwgLWB0b3RhbF9ob3VzaW5nX3VuaXRzYCwgLWB0b3RhbF92YWNhbnRfaG91c2Vob2xkc2AsIC1gdG90YWxfd2hpdGVgLCAtYG1lZGlhbl9ob3VzZWhvbGRfaW5jb21lYCwgLWB0b3RhbF9ncmFkdWF0ZV9kZWdyZWVfaG9sZGVyc2ApCmBgYAoKCkZlYXR1cmUgb2YgMjAwMCB0b3RhbCBwb3B1bGF0aW9uIGRhdGEgaXMgcGxvdHRlZC4KYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpnZ3Bsb3QoKSsKICBnZW9tX3NmKGRhdGEgPSBjdmFtXzIwMDBfY2Vuc3VzLCAKICAgICAgICAgIGFlcyhmaWxsID0gdG90YWxfcG9wdWxhdGlvbikpICsgbWFwVGhlbWUKYGBgCgpUbyBqb2luIHRoZSBkZW1vZ3JhcGhpYyBkYXRhIHRvIGZpc2huZXQsIGEgc3BhdGlhbCBqb2luIHdvdWxkIGJlIGluYXBwcm9wcmlhdGUgYXMgaXQgd291bGQgYXNzaWduIHRoZSBzYW1lIGRlbW9ncmFwaGljIGZlYXR1cmUgdmFsdWUgZnJvbSBvbmUgdHJhY3QgdG8gdGhlIG1hbnkgaW50ZXJzZWN0aW5nIGdyaWQgY2VsbHMuIEluc3RlYWQsIHRoZSBhcmVhIHdlaWdodGVkIGludGVycG9sYXRpb24gZnVuY3Rpb24sIGBzdF9pbnRlcnBvbGF0ZV9hd2AsIGFzc2lnbnMgYSBwcm9wb3J0aW9uIG9mIGEgdHJhY3TigJlzIGRlbW9ncmFwaGljIGZlYXR1cmUgdG8gYSBncmlkIGNlbGwgd2VpZ2h0ZWQgYnkgdGhlIHByb3BvcnRpb24gb2YgdGhlIHRyYWN0IHRoYXQgaW50ZXJzZWN0cyB0aGUgZ3JpZCBjZWxsLiBUaGlzIHdvcmtzIGJlc3Qgb2YgY291cnNlLCB3aGVuIHdlIGFzc3VtZSB0aGF0IHRoZSBibG9jayBncm91cCBwb3B1bGF0aW9uIGlzIHVuaWZvcm1seSBkaXN0cmlidXRlZCBhY3Jvc3MgdGhlIGJsb2NrIGdyb3VwLiBUaGlzIGlzIHR5cGljYWxseSBub3QgYSBncmVhdCBhc3N1bXB0aW9uLiBIb3dldmVyLCBpdCBpcyBhIHJlYXNvbmFibGUgaGVyZSBwYXJ0aWN1bGFybHkgZ2l2ZW4gZGVtb2dyYXBoaWMgZmVhdHVyZXMgaW4gYSByZWdyZXNzaW9uIGFuZCBub3QgYW4gb3V0Y29tZSB0aGF0IG5lZWRzIHRvIGJlIG1lYXN1cmVkIHdpdGggc2lnbmlmaWNhbnQgcHJlY2lzaW9uLgoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpDdmlsbGVNU0EgPC0KICBDdmlsbGVNU0EgJT4lCiAgcm93bmFtZXNfdG9fY29sdW1uKCJmaXNobmV0SUQiKSAlPiUgCiAgbXV0YXRlKGZpc2huZXRJRCA9IGFzLm51bWVyaWMoZmlzaG5ldElEKSkgJT4lCiAgZHBseXI6OnNlbGVjdChmaXNobmV0SUQpCgojIFRyYW5zZm9ybSBQcm9qZWN0aW9uIG9mIENlbnN1cyBEYXRhCmN2YW1fMjAwMF9jZW5zdXMgPC0gY3ZhbV8yMDAwX2NlbnN1cyAlPiUgCiAgc3RfdHJhbnNmb3JtKHN0X2NycyhDdmlsbGVNU0EpKQoKY3ZhbV8yMDIwX2NlbnN1cyA8LSBjdmFtXzIwMjBfY2Vuc3VzICU+JSAKICBzdF90cmFuc2Zvcm0oc3RfY3JzKEN2aWxsZU1TQSkpCmBgYAoKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KIyBKb2luIGludGVycG9sYXRlZCAyMDAwIGNlbnN1cyBkYXRhIHdpdGggZmlzaG5ldAoKIyAyMDAwIGludGVycG9sYXRpb24gZnVuY3Rpb24KaW50ZXJwb2xhdGVfY29sdW1uMjAwMCA8LSBmdW5jdGlvbihjb2x1bW5fbmFtZSkgewogIAogIGludGVycG9sYXRlZF9kYXRhIDwtCiAgICBzdF9pbnRlcnBvbGF0ZV9hdyhjdmFtXzIwMDBfY2Vuc3VzW2NvbHVtbl9uYW1lXSwgQ3ZpbGxlTVNBLCBleHRlbnNpdmU9VFJVRSkgJT4lCiAgICBhcy5kYXRhLmZyYW1lKC4pICU+JQogICAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJmaXNobmV0SUQiKSAlPiUKICAgIGxlZnRfam9pbihDdmlsbGVNU0EgJT4lCiAgICAgICAgICAgICAgICBtdXRhdGUoZmlzaG5ldElEID0gYXMuY2hhcmFjdGVyKGZpc2huZXRJRCkpLAogICAgICAgICAgICAgIC4sIGJ5PWMoImZpc2huZXRJRCI9J2Zpc2huZXRJRCcpKSAlPiUgCiAgICBtdXRhdGUoISFjb2x1bW5fbmFtZSA6PSByZXBsYWNlX25hKCEhc3ltKGNvbHVtbl9uYW1lKSwgMCkpICU+JQogICAgZHBseXI6OnNlbGVjdChjb2x1bW5fbmFtZSkKICAKICByZXR1cm4oaW50ZXJwb2xhdGVkX2RhdGEpCn0KCmZpc2huZXRQb3B1bGF0aW9uMDAgPC0gaW50ZXJwb2xhdGVfY29sdW1uMjAwMCgndG90YWxfcG9wdWxhdGlvbicpCmZpc2huZXRISHVuaXQwMCA8LSBpbnRlcnBvbGF0ZV9jb2x1bW4yMDAwKCd0b3RhbF9ob3VzaW5nX3VuaXRzJykKZmlzaG5ldFZhY0hIMDAgPC0gaW50ZXJwb2xhdGVfY29sdW1uMjAwMCgndG90YWxfdmFjYW50X2hvdXNlaG9sZHMnKQpmaXNobmV0V2hpdGVQMDAgPC0gaW50ZXJwb2xhdGVfY29sdW1uMjAwMCgndG90YWxfd2hpdGUnKQpmaXNobmV0R3JhZHVhdGUwMCA8LSBpbnRlcnBvbGF0ZV9jb2x1bW4yMDAwKCd0b3RhbF9ncmFkdWF0ZV9kZWdyZWVfaG9sZGVycycpCgojIDIwMjAgaW50ZXJwb2xhdGlvbiBmdW5jdGlvbgppbnRlcnBvbGF0ZV9jb2x1bW4yMDIwIDwtIGZ1bmN0aW9uKGNvbHVtbl9uYW1lKSB7CiAgCiAgaW50ZXJwb2xhdGVkX2RhdGEgPC0KICAgIHN0X2ludGVycG9sYXRlX2F3KGN2YW1fMjAyMF9jZW5zdXNbY29sdW1uX25hbWVdLCBDdmlsbGVNU0EsIGV4dGVuc2l2ZT1UUlVFKSAlPiUKICAgIGFzLmRhdGEuZnJhbWUoLikgJT4lCiAgICByb3duYW1lc190b19jb2x1bW4odmFyID0gImZpc2huZXRJRCIpICU+JQogICAgbGVmdF9qb2luKEN2aWxsZU1TQSAlPiUKICAgICAgICAgICAgICAgIG11dGF0ZShmaXNobmV0SUQgPSBhcy5jaGFyYWN0ZXIoZmlzaG5ldElEKSksCiAgICAgICAgICAgICAgLiwgYnk9YygiZmlzaG5ldElEIj0nZmlzaG5ldElEJykpICU+JSAKICAgIG11dGF0ZSghIWNvbHVtbl9uYW1lIDo9IHJlcGxhY2VfbmEoISFzeW0oY29sdW1uX25hbWUpLCAwKSkgJT4lCiAgICBkcGx5cjo6c2VsZWN0KGNvbHVtbl9uYW1lKQogIAogIHJldHVybihpbnRlcnBvbGF0ZWRfZGF0YSkKfQoKZmlzaG5ldFBvcHVsYXRpb24yMCA8LSBpbnRlcnBvbGF0ZV9jb2x1bW4yMDIwKCd0b3RhbF9wb3B1bGF0aW9uMjAyMCcpCmZpc2huZXRISHVuaXQyMCA8LSBpbnRlcnBvbGF0ZV9jb2x1bW4yMDIwKCd0b3RhbF9ob3VzaW5nX3VuaXRzMjAyMCcpCmZpc2huZXRXaGl0ZVAyMCA8LSBpbnRlcnBvbGF0ZV9jb2x1bW4yMDIwKCd0b3RhbF93aGl0ZTIwMjAnKQoKZmlzaG5ldF9wb3BfY2hhbmdlXzAwXzIwIDwtIAogIGNiaW5kKGZpc2huZXRQb3B1bGF0aW9uMDAsZmlzaG5ldFBvcHVsYXRpb24yMCkgJT4lCiAgZHBseXI6OnNlbGVjdCh0b3RhbF9wb3B1bGF0aW9uLHRvdGFsX3BvcHVsYXRpb24yMDIwKSAlPiUKICBtdXRhdGUocG9wX0NoYW5nZV8wMF8yMCA9IHRvdGFsX3BvcHVsYXRpb24yMDIwIC0gdG90YWxfcG9wdWxhdGlvbikKCiNwbG90IGRhdGEgZnJvbSAyMDAwCmdncGxvdCgpKwogIGdlb21fc2YoZGF0YSA9IGZpc2huZXRQb3B1bGF0aW9uMDAsIAogICAgICAgICAgYWVzKGZpbGwgPSB0b3RhbF9wb3B1bGF0aW9uKSwgY29sb3IgPSAidHJhbnNwYXJlbnQiKSsgbWFwVGhlbWUKYGBgCgoKIyMgMi41LiBIaWdod2F5IERpc3RhbmNlCgpBY2Nlc3NpYmlsaXR5IGlzIGEga2V5IGRldGVybWluYW50IG9mIGRldmVsb3BtZW50IHBvdGVudGlhbCBwYXJ0aWN1bGFybHkgaW4gYSBzcHJhd2xpbmcgY2l0eSBsaWtlIENoYXJsb3R0ZXN2aWxsZSBNU0EuIEFjY2Vzc2liaWxpdHkgZmVhdHVyZXMgYXJlIGVuZ2luZWVyZWQgYnkgbWVhc3VyaW5nIGRpc3RhbmNlIGZyb20gZWFjaCBncmlkIGNlbGwgdG8gaXRzIG5lYXJlc3QgaGlnaHdheS4KCkZpcnN0IGhpZ2h3YXkgdmVjdG9ycyBhcmUgZG93bmxvYWRlZCBmcm9tIHRoZSBWaXJnaW5pYSBTdGF0ZSBvcGVuIGRhdGEgd2Vic2l0ZSBpbiBgZ2VvanNvbmAgZm9ybWF0OyBwcm9qZWN0ZWQgYW5kIHN1YnNldCB0byB0aGUgc3Vic2V0IHVzaW5nIGBzdF9pbnRlcnNlY3Rpb25gLiBCZWxvdywgbmV3IGRldmVsb3BtZW50IGlzIG1hcHBlZCB3aXRoIHRoZSBoaWdod2F5IG92ZXJsYXkuCgpgYGB7ciByZWFkIGFuZCBwbG90IGhpZ2h3YXksIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZT0gRkFMU0UscmVzdWx0cyA9ICJoaWRlIn0KQ3ZpbGxlSGlnaHdheXMgPC0KICAjc3RfcmVhZCgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL1lpbmd0b25nLVovc3ByYXdsLWZvcmVjYXN0aW5nL2I1MjhkNzI1ZWFlYTVkZDc3OGM0MWZhNjQ0YjVlNmY3MWQ2MjkxYjIvZGF0YS9DdmlsbGVfSGlnaHdheXMuZ2VvanNvbiIpICU+JQogIHN0X3JlYWQoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9ZaW5ndG9uZy1aL3NwcmF3bC1mb3JlY2FzdGluZy85OTJkYjBhMmU4NTRjNzY0ZGYyYmJhZmNhYWM2ZDQyNDk3NWQ0YmRjL2RhdGEvQ3ZpbGxlX0hpZ2h3YXlzMi5nZW9qc29uIikgJT4lCiAgc3RfdHJhbnNmb3JtKHN0X2NycyhDdmlsbGVNU0EpKSAlPiUKICBzdF9pbnRlcnNlY3Rpb24oQ3ZpbGxlTVNBKQoKZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoZGF0YT1maXNobmV0LCAKICAgICAgICAgICAgIGFlcyh4PXh5QyhmaXNobmV0KVssMV0sIHk9eHlDKGZpc2huZXQpWywyXSxjb2xvdXI9bGNfY2hhbmdlKSxzaXplPTAuMSkgKwogIGdlb21fc2YoZGF0YT1DdmlsbGVIaWdod2F5cywgc2l6ZSA9IDEpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUyLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIk5vIENoYW5nZSIsIk5ldyBEZXZlbG9wbWVudCIpKSArCiAgbGFicyh0aXRsZSA9ICJOZXcgRGV2ZWxvcG1lbnQgYW5kIEhpZ2h3YXlzIiwKICAgICAgIHN1YnRpdGxlID0gIkFzIGZpc2huZXQgY2VudHJvaWRzIikgKwogIG1hcFRoZW1lCmBgYAoKQmVsb3cgYXJlIHNvbWUgZ3JlYXQgci1iYXNlZCByYXN0ZXIgc2tpbGxzLiBUaGUgZGlzdGFuY2UgZnJvbSBlYWNoIGdyaWQgY2VsbCB0byBpdHMgbmVhcmVzdCBoaWdod2F5IHNlZ21lbnQgaXMgbWVhc3VyZWQuCgpGaXJzdCwgdGhlIGhpZ2h3YXkgbGF5ZXIgaXMgY29udmVydGVkIHRvIHJhc3Rlci4gVGhpcyBpcyBkb25lIGJ5IGNyZWF0aW5nIGFuIGBlbXB0eVJhc3RlcmAgb2YgYE5BYCBncmlkIGNlbGxzIGF0IHRoZSBzYW1lIHNwYXRpYWwgZXh0ZW50IGFzIGBsY19jaGFuZ2VgLiBUaGVuLCBgaGlnaHdheV9yYXN0ZXJgIGlzIGNyZWF0ZWQgYnkgY29udmVydGluZyBgQ3ZpbGxlSGlnaHdheXNgIHRvIGBzcGAgZm9ybSBhbmQgdGhlbiB0byBhcHBseWluZyBgcmFzdGVyaXplYC4gVGhlIHJhc3RlciBpcyB0aGVuIGNvbnZlcnRlZCB0byBwb2ludHMgd2l0aCBgcmFzdGVyVG9Qb2ludHNgIGFuZCBgc3RfYXNfc2ZgLCB0aGVuIGBhZ2dyZWdhdGVgIGlzIHVzZWQgdG8gY2FsY3VsYXRlIG1lYW4gZGlzdGFuY2UgYnkgZ3JpZCBjZWxsLgoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQplbXB0eVJhc3RlciA8LSBsY19jaGFuZ2UKZW1wdHlSYXN0ZXJbXSA8LSBOQQoKaGlnaHdheV9yYXN0ZXIgPC0gCiAgYXMoQ3ZpbGxlSGlnaHdheXMsJ1NwYXRpYWwnKSAlPiUKICByYXN0ZXJpemUoLixlbXB0eVJhc3RlcikKCmhpZ2h3YXlfcmFzdGVyX2Rpc3RhbmNlIDwtIGRpc3RhbmNlKGhpZ2h3YXlfcmFzdGVyKQpuYW1lcyhoaWdod2F5X3Jhc3Rlcl9kaXN0YW5jZSkgPC0gImRpc3RhbmNlX2hpZ2h3YXlzIgoKaGlnaHdheVBvaW50cyA8LQogIHJhc3RlclRvUG9pbnRzKGhpZ2h3YXlfcmFzdGVyX2Rpc3RhbmNlKSAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgc3RfYXNfc2YoY29vcmRzID0gYygieCIsICJ5IiksIGNycyA9IHN0X2NycyhDdmlsbGVNU0EpKQoKaGlnaHdheVBvaW50c19maXNobmV0IDwtIAogIGFnZ3JlZ2F0ZShoaWdod2F5UG9pbnRzLCBDdmlsbGVNU0EsIG1lYW4pICU+JQogIG11dGF0ZShkaXN0YW5jZV9oaWdod2F5cyA9IGlmZWxzZShpcy5uYShkaXN0YW5jZV9oaWdod2F5cyksMCxkaXN0YW5jZV9oaWdod2F5cykpCgpnZ3Bsb3QoKSArCiAgZ2VvbV9zZihkYXRhPUN2aWxsZU1TQSkgKwogIGdlb21fcG9pbnQoZGF0YT1oaWdod2F5UG9pbnRzX2Zpc2huZXQsIGFlcyh4PXh5QyhoaWdod2F5UG9pbnRzX2Zpc2huZXQpWywxXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHk9eHlDKGhpZ2h3YXlQb2ludHNfZmlzaG5ldClbLDJdLCAKICAgICAgICAgICAgICAgICBjb2xvdXI9ZmFjdG9yKG50aWxlKGRpc3RhbmNlX2hpZ2h3YXlzLDUpKSksc2l6ZT0xLjUpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGU1LAogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPXN1YnN0cihxdWludGlsZUJyZWFrcyhoaWdod2F5UG9pbnRzX2Zpc2huZXQsImRpc3RhbmNlX2hpZ2h3YXlzIiksMSw4KSwKICAgICAgICAgICAgICAgICAgICAgIG5hbWU9IlF1aW50aWxlXG5CcmVha3MiKSArCiAgZ2VvbV9zZihkYXRhPUN2aWxsZUhpZ2h3YXlzLCBjb2xvdXIgPSAicmVkIikgKwogIGxhYnModGl0bGUgPSAiRGlzdGFuY2UgdG8gSGlnaHdheXMiLAogICAgICAgc3VidGl0bGUgPSAiQXMgZmlzaG5ldCBjZW50cm9pZHM7IEhpZ2h3YXlzIHZpc3VhbGl6ZWQgaW4gcmVkIikgKwogIG1hcFRoZW1lCmBgYAoKIyMgMi42LiBBZGQgU2xvcGUgVmFyaWFibGUKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLHJlc3VsdHMgPSAiaGlkZSJ9ClNsb3BlUmFzdGVyIDwtIHJhc3RlcigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL1lpbmd0b25nLVovc3ByYXdsLWZvcmVjYXN0aW5nLzlmODEwZWM4ZTFlOTc3NGM1MWQ4MzE5NGUyMjg0OGU4MjQwZDI2ZjcvZGF0YS9DdmlsbGVfU2xvcGUudGlmIikKCiMgQ2hlY2sgdGhlIENSUyBvZiB0aGUgcmFzdGVyIGFuZCB0aGUgZmlzaG5ldCBkYXRhCnJhc3Rlcl9jcnMgPC0gY3JzKFNsb3BlUmFzdGVyKQpmaXNobmV0X2NycyA8LSBzdF9jcnMoZmlzaG5ldCkKCiMgSWYgQ1JTIGRvZXNuJ3QgbWF0Y2gsIHJlcHJvamVjdCB0aGUgZmlzaG5ldCBkYXRhIHRvIG1hdGNoIHRoZSByYXN0ZXIgQ1JTCmlmIChyYXN0ZXJfY3JzICE9IGZpc2huZXRfY3JzKSB7CiAgZmlzaG5ldCA8LSBzdF90cmFuc2Zvcm0oZmlzaG5ldCwgcmFzdGVyX2Nycyl9CgojIENhbGN1bGF0ZSB0aGUgbWVhbiByYXN0ZXIgdmFsdWVzIHdpdGhpbiBlYWNoIGZpc2huZXQgY2VsbAptZWFuX3ZhbHVlcyA8LSBleGFjdF9leHRyYWN0KFNsb3BlUmFzdGVyLCBmaXNobmV0LCBmdW4gPSAnbWVhbicpCgojIEFkZCB0aGUgbWVhbiB2YWx1ZXMgYXMgYSBuZXcgY29sdW1uIHRvIHRoZSBmaXNobmV0IGRhdGEKZmlzaG5ldCRzbG9wZSA8LSBtZWFuX3ZhbHVlcwoKIyBUcmFuc2Zvcm0gdGhlIGZpc2huZXQgYmFjayBFUFNHOjIyODQKZmlzaG5ldCA8LSBzdF90cmFuc2Zvcm0oZmlzaG5ldCwgMjI4NCkKYGBgCgojIyAyLjcuIFRoZSBTcGF0aWFsIExhZyBvZiBEZXZlbG9wbWVudAoKT3VyIG1vZGVsIGh5cG90aGVzaXplcyB0aGF0IGRldmVsb3BtZW50IGRlbWFuZCBwYXJ0bHkgZGVwZW5kcyBvbiBleGlzdGluZyBkZXZlbG9wbWVudCBwYXR0ZXJucy4gQWNjZXNzaWJpbGl0eSBwbGF5cyBhIHNpZ25pZmljYW50IHJvbGUgaW4gdHJhZGl0aW9uYWwgJ2JpZC1yZW50JyBlY29ub21pYyBtb2RlbHMgb2YgZGV2ZWxvcG1lbnQuIEhvd2V2ZXIsIHRoaXMgbW9kZWwgYXNzdW1lcyBzaGFyZWQgcHJlZmVyZW5jZXMgZm9yIGNlbnRyYWwgY2l0eSBhY2Nlc3MsIHdoaWNoIG1heSBub3QgaG9sZCB0cnVlIGluIHNwcmF3bGluZyByZWdpb25zIGxpa2UgQ2hhcmxvdHRlc3ZpbGxlIE1TQSwgd2hlcmUgc3VidXJiYW4gbG9jYXRpb25zIGFyZSBkZXNpcmFibGUuCgpUbyBmb3JlY2FzdCBncm93dGgsIGZlYXR1cmVzIG11c3QgYmUgY3JlYXRlZCB0byBhc3NvY2lhdGUgdGhlc2UgcGF0dGVybnMgd2l0aCBkZXZlbG9wbWVudC4gQWNjZXNzaWJpbGl0eSBpcyBtZWFzdXJlZCB2aWEgc3BhdGlhbCBsYWcsIGh5cG90aGVzaXppbmcgdGhhdCBuZXcgZGV2ZWxvcG1lbnQgZGVwZW5kcyBvbiBkaXN0YW5jZSB0byBleGlzdGluZyBkZXZlbG9wbWVudC4gVGhlIGF2ZXJhZ2UgZGlzdGFuY2UgZnJvbSBlYWNoIGdyaWQgY2VsbCB0byBpdHMgdHdvIG5lYXJlc3QgZGV2ZWxvcGVkIG5laWdoYm9yaW5nIGdyaWQgY2VsbHMgaW4gMjAwMSBpcyBjYWxjdWxhdGVkIHVzaW5nIHRoZSBubl9mdW5jdGlvbi4KCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KIyBUaGUgZnVuY3Rpb24gYmVsb3cgY2FsY3VsYXRlcyBhdmVyYWdlIG5lYXJlc3QgbmVpZ2hib3IgZGlzdGFuY2UgYmV0d2VlbiBrIHBvaW50IGxheWVycy4gVGhlIGZpcnN0IHBhcmFtZXRlciBzcGVjaWZpZXMgY29vcmRpbmF0ZXMgdGhhdCB3ZSB3YW50IHRvIGBtZWFzdXJlRnJvbWAsIGluIHRoaXMgY2FzZSwgYGZpc2huZXRgIGNlbnRyb2lkcy4gVGhlIHNlY29uZCwgaW5kaWNhdGVzIHRoZSBwb2ludCBsYXllciB3ZSB3aXNoIHRvIGBtZWFzdXJlVG9gLgoKbm5fZnVuY3Rpb24gPC0gZnVuY3Rpb24obWVhc3VyZUZyb20sbWVhc3VyZVRvLGspIHsKICAjY29udmVydCB0aGUgc2YgbGF5ZXJzIHRvIG1hdHJpY2VzCiAgbWVhc3VyZUZyb21fTWF0cml4IDwtCiAgICBhcy5tYXRyaXgobWVhc3VyZUZyb20pCiAgbWVhc3VyZVRvX01hdHJpeCA8LQogICAgYXMubWF0cml4KG1lYXN1cmVUbykKICBubiA8LSAgIAogICAgZ2V0LmtubngobWVhc3VyZVRvLCBtZWFzdXJlRnJvbSwgaykkbm4uZGlzdAogICAgb3V0cHV0IDwtCiAgICBhcy5kYXRhLmZyYW1lKG5uKSAlPiUKICAgIHJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAidGhpc1BvaW50IikgJT4lCiAgICBnYXRoZXIocG9pbnRzLCBwb2ludF9kaXN0YW5jZSwgVjE6bmNvbCguKSkgJT4lCiAgICBhcnJhbmdlKGFzLm51bWVyaWModGhpc1BvaW50KSkgJT4lCiAgICBncm91cF9ieSh0aGlzUG9pbnQpICU+JQogICAgc3VtbWFyaXplKHBvaW50RGlzdGFuY2UgPSBtZWFuKHBvaW50X2Rpc3RhbmNlKSkgJT4lCiAgICBhcnJhbmdlKGFzLm51bWVyaWModGhpc1BvaW50KSkgJT4lIAogICAgZHBseXI6OnNlbGVjdCgtdGhpc1BvaW50KSAlPiUKICAgIHB1bGwoKQogIAogIHJldHVybihvdXRwdXQpICAKfQpgYGAKCgpOZXh0LCB0aGUgZnVuY3Rpb24gYXBwZW5kaW5nIHRoZSBsYWcgZGlzdGFuY2UgdG8gYGZpc2huZXRgLiBUaGVyZSBhcmUgMyBpbnB1dHMuIFRoZSBgZmlzaG5ldGAgd2hpY2ggaXMgY29udmVydGVkIHRvIGEgY29vcmRpbmF0ZSBkYXRhIGZyYW1lIHdpdGggdGhlIGB4eUNgIGZ1bmN0aW9uLiAyMDAxIGRldmVsb3BlZCBhcmVhcyBhcmUgY3JlYXRlZCB1c2luZyBgZmlsdGVyYC4gVGhlIG1hcCBiZWxvdyBpbGx1c3RyYXRlcyByZWxhdGl2ZSBhY2Nlc3NpYmlsaXR5IGZyb20gZXZlcnkgZ3JpZCBjZWxsIHRvIG5lYXJieSBkZXZlbG9wbWVudC4KCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KZmlzaG5ldCRsYWdEZXZlbG9wbWVudCA8LQogICAgbm5fZnVuY3Rpb24oeHlDKGZpc2huZXQpLAogICAgICAgICAgICAgICAgeHlDKGZpbHRlcihhZ2dyZWdhdGVkUmFzdGVycyxkZXZlbG9wZWQ9PTEpKSwKICAgICAgICAgICAgICAgIDIpCgpnZ3Bsb3QoKSArCiAgZ2VvbV9zZihkYXRhPUN2aWxsZU1TQSkgKwogIGdlb21fcG9pbnQoZGF0YT1maXNobmV0LCAKICAgICAgICAgICAgIGFlcyh4PXh5QyhmaXNobmV0KVssMV0sIHk9eHlDKGZpc2huZXQpWywyXSwgCiAgICAgICAgICAgICAgICAgY29sb3VyPWZhY3RvcihudGlsZShsYWdEZXZlbG9wbWVudCw1KSkpLCBzaXplPTAuMDEpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGU1LAogICAgICAgICAgICAgICAgICAgICBsYWJlbHM9c3Vic3RyKHF1aW50aWxlQnJlYWtzKGZpc2huZXQsImxhZ0RldmVsb3BtZW50IiksMSw3KSwKICAgICAgICAgICAgICAgICAgICAgbmFtZT0iUXVpbnRpbGVcbkJyZWFrcyIpICsKICBsYWJzKHRpdGxlID0gIlNwYXRpYWwgTGFnIHRvIDIwMDEgRGV2ZWxvcG1lbnQiLAogICAgICAgc3VidGl0bGUgPSAiQXMgZmlzaG5ldCBjZW50cm9pZHMiKSArCiAgbWFwVGhlbWUKYGBgCgojIyAyLjguIE1TQSBDb3VudGllcwoKVGhlIGB0aWdyaXNgIHBhY2thZ2UgYWxsb3dzIFZpcmdpbmlhIGNvdW50eSBnZW9tZXRyaWVzIHRvIGJlIGRvd25sb2FkZWQuIEEgc3BhdGlhbCBzdWJzZXQgcmV0dXJucyBvbmx5IHRoZSBjb3VudGllcyBpbiB0aGUgTVNBLiBOb3RlIHRoYXQgdGhlIHN1YnNldCBpbmNsdWRlcyBhIG5lZ2F0aXZlIDEwMDBmdCBgc3RfYnVmZmVyYC4gVGhpcyBpcyBkb25lIGJlY2F1c2UgdGhlIHNwYXRpYWwgZXh0ZW50IG9mIGBDdmlsbGVNU0FgIGludGVyc2VjdHMgY291bnR5IGJvdW5kYXJpZXMgdGhhdCBhcmUgYWN0dWFsbHkgb3V0c2lkZSBvZiBvdXIgc3R1ZHkgYXJlYS4gQnVmZmVyaW5nIGBDdmlsbGVNU0FgIHNsaWdodGx5IGxpbWl0cyB0aGUgaW50ZXJzZWN0aW9uIHJhbmdlIHRvIG9ubHkgdGhvc2UgY291bnRpZXMgaW4gdGhlIHN0dWR5IGFyZWEuCgpPbmNlIGBzdHVkeUFyZWFDb3VudGllc2AgaXMgY3JlYXRlZCwgaXQgaXMgYHN0X2pvaW5gZWQgd2l0aCBgZGF0YCBzdWNoIHRoYXQgZWFjaCBncmlkIGNlbGxzIGtub3dzIHdoaWNoIGNvdW50eSBpdOKAmXMgaW4uCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIHJlc3VsdHMgPSAiaGlkZSJ9Cm9wdGlvbnModGlncmlzX2NsYXNzID0gInNmIikKCnN0dWR5QXJlYUNvdW50aWVzIDwtIAogIGNvdW50aWVzKCJWaXJnaW5pYSIpICU+JQogIHN0X3RyYW5zZm9ybShzdF9jcnMoQ3ZpbGxlTVNBKSkgJT4lCiAgZHBseXI6OnNlbGVjdChOQU1FKSAlPiUKICAuW3N0X2J1ZmZlcihDdmlsbGVNU0EsLTEpLCAsIG9wPXN0X2ludGVyc2VjdHNdIAoKYGBgCgoKIyMgMi45LiBDcmVhdGUgdGhlIEZpbmFsIERhdGFzZXQKClRoZSBsYXN0IHN0ZXAgaXMgdG8gYnJpbmcgdG9nZXRoZXIgYWxsIHRoZSBkaXNwYXJhdGUgZmVhdHVyZSBsYXllcnMgaW50byBhIGZpbmFsIGRhdGFzZXQgdGhhdCBjYW4gYmUgdXNlZCBmb3IgYW5hbHlzaXMuIFRoZSB2YXJpb3VzIGZpc2huZXQgbGF5ZXJzIGFyZSBgY2JpbmRgIHRvZ2V0aGVyLCBuZWVkZWQgZmVhdHVyZXMgYXJlIGV4dHJhY3RlZCBhbmQgdGhlIGZpbmFsIGZpc2huZXQsIGBkYXRgIGlzIHRoZW4gam9pbmVkIHdpdGggYHN0dWR5QXJlYUNvdW50aWVzYCB0byBhc3NpZ24gZWFjaCBncmlkIGNlbGwgdG8gYSBjb3VudHkuIGBkZXZlbG9wZWQxMGAgaXMgY3JlYXRlZCB0byBkZXNpZ25hdGUgdGhvc2UgYXJlYXMgdGhhdCBoYXZlIGFscmVhZHkgYmVlbiBkZXZlbG9wZWQgdGhyb3VnaCAyMDE5LiBGaW5hbGx5LCBhbnkgZ3JpZCBjZWxsIHRoYXQgaGFzIGEgYHdhdGVyYCBsYW5kIGNvdmVyIGRlc2lnbmF0aW9uIGlzIHJlbW92ZWQuCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmRhdCA8LSAKICBjYmluZCgKICAgIGZpc2huZXQsIGhpZ2h3YXlQb2ludHNfZmlzaG5ldCxmaXNobmV0R3JhZHVhdGUwMCwKICAgIGZpc2huZXRISHVuaXQwMCxmaXNobmV0UG9wdWxhdGlvbjAwLGZpc2huZXRWYWNISDAwLAogICAgZmlzaG5ldFdoaXRlUDAwLGZpc2huZXRQb3B1bGF0aW9uMjAsZmlzaG5ldF9wb3BfY2hhbmdlXzAwXzIwLGFnZ3JlZ2F0ZWRSYXN0ZXJzLAogICAgZmlzaG5ldEhIdW5pdDIwLCBmaXNobmV0V2hpdGVQMjApICU+JQogIGRwbHlyOjpzZWxlY3QobGNfY2hhbmdlLCBkZXZlbG9wZWQsIGZvcmVzdCwgZmFybSwgd2V0bGFuZHMsIG90aGVyVW5kZXZlbG9wZWQsIHNsb3BlLHdhdGVyLAogICAgICAgICAgICAgICAgdG90YWxfcG9wdWxhdGlvbix0b3RhbF9ncmFkdWF0ZV9kZWdyZWVfaG9sZGVycyx0b3RhbF9ob3VzaW5nX3VuaXRzLAogICAgICAgICAgICAgICAgdG90YWxfdmFjYW50X2hvdXNlaG9sZHMsdG90YWxfd2hpdGUsdG90YWxfcG9wdWxhdGlvbjIwMjAscG9wX0NoYW5nZV8wMF8yMCxkaXN0YW5jZV9oaWdod2F5cywgbGFnRGV2ZWxvcG1lbnQsIHRvdGFsX2hvdXNpbmdfdW5pdHMyMDIwLCB0b3RhbF93aGl0ZTIwMjApICU+JQogIHN0X2pvaW4oc3R1ZHlBcmVhQ291bnRpZXMpICU+JQogIG11dGF0ZShOQU1FID0gaWZlbHNlKE5BTUUgPT0gIkNoYXJsb3R0ZXN2aWxsZSIsICJDaGFybG90dGVzdmlsbGUiLCAiQWxiZW1hcmxlIikpICU+JSAKICBtdXRhdGUoZGV2ZWxvcGVkMTAgPSBpZmVsc2UobGNfY2hhbmdlID09IDEgJiBkZXZlbG9wZWQgPT0gMSwgMCwgZGV2ZWxvcGVkKSkgJT4lCiAgZmlsdGVyKHdhdGVyID09IDApIAoKc3R1ZHlBcmVhQ291bnRpZXMgPC0gc3R1ZHlBcmVhQ291bnRpZXMgJT4lIAogIGZpbHRlcihOQU1FID09ICJBbGJlbWFybGUiIHwgTkFNRSA9PSAiQ2hhcmxvdHRlc3ZpbGxlIikKYGBgCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmdncGxvdCgpICsKICBnZW9tX3NmKGRhdGE9c3R1ZHlBcmVhQ291bnRpZXMsCiAgICAgICAgICBhZXMoZmlsbCA9IE5BTUUpKSArCiAgbGFicyh0aXRsZSA9ICJTdHVkeSBBcmVhIENvdW50aWVzIikgKwogIG1hcFRoZW1lCmBgYAoKIyAzLiBFeHBsb3JhdG9yeSBBbmFseXNpcwoKSW4gdGhpcyBzZWN0aW9uIHdlIGV4cGxvcmUgdGhlIGV4dGVudCB0byB3aGljaCBlYWNoIGZlYXR1cmVzIGlzIGFzc29jaWF0ZWQgd2l0aCBkZXZlbG9wbWVudCBjaGFuZ2UuIElmIHRoZSBnb2FsIHdhcyB0byBwcmVkaWN0IGEgY29udGludW91cyB2YXJpYWJsZSwgc2NhdHRlcnBsb3RzIGFuZCBjb3JyZWxhdGlvbiBjb2VmZmljaWVudHMgbWFrZSB0aGlzIHByb2Nlc3Mgc3RyYWlnaHRmb3J3YXJkIGFuZCByZWxhdGl2ZWx5IGVhc3kgdG8gZXhwbGFpbiB0byBhIG5vbi10ZWNobmljYWwgZGVjaXNvbiBtYWtlci4KCkluIHRoaXMgY2FzZSBob3dldmVyLCB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIGlzIGEgYmluYXJ5IG91dGNvbWUgLSBlaXRoZXIgYSBncmlkIGNlbGwgd2FzIGRldmVsb3BlZCBiZXR3ZWVuIDIwMDEgYW5kIDIwMTEgb3IgaXQgd2FzbuKAmXQuIEluIHRoaXMgY2FzZSwgdGhlIHJlbGV2YW50IHF1ZXN0aW9uIGlzIHdoZXRoZXIgZm9yIGEgZ2l2ZW4gZmVhdHVyZSwgdGhlcmUgaXMgYSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGRpZmZlcmVuY2UgYmV0d2VlbiBhcmVhcyB0aGF0IGNoYW5nZWQgYW5kIGFyZWFzIHRoYXQgZGlkIG5vdC4gVGhlc2UgZGlmZmVyZW5jZXMgYXJlIGV4cGxvcmVkIGluIGEgc2V0IG9mIHBsb3RzIGJlbG93LiBGb3IgbW9kZWxzIHdpdGggbG90cyBvZiBmZWF0dXJlcywgdGhlc2UgcGxvdHMgY291bGQgYmUgY29tcGxpbWVudCBieSBhIHNlcmllcyBvZiBkaWZmZXJlbmNlIGluIG1lYW5zIHN0YXRpc3RpY2FsIHRlc3RzLgoKVGhlIGJlbG93IGNvZGUgYmxvY2sgYHNlbGVjdGBzIHRoZSBoaWdod2F5cyBhbmQgc3BhdGlhbCBsYWcgZmVhdHVyZXMsIGNvbnZlcnRzIGVhY2ggdG8gbG9uZyBmb3JtIGFuZCBwbG90cyBlYWNoIGFzIGJhciBwbG90cy4gTm90ZSB0aGF0IGBnZW9tX2JhcmAgY2FsY3VsYXRlcyB0aGUgYG1lYW5gLiBUaGUgbWVhbiBkaXN0YW5jZV9oaWdod2F5cyBpcyBzaWduaWZpY2FudGx5IGxvd2VyIGZvciB0aGUgYE5ldyBEZXZlbG9wbWVudGAgY2F0ZWdvcnkgY29tcGFyZWQgdG8gdGhlIGBObyBDaGFuZ2VgIGNhdGVnb3J5LCBpdCBpbmRpY2F0ZXMgdGhhdCBuZXcgZGV2ZWxvcG1lbnRzIHRlbmQgdG8gYmUgY2xvc2VyIHRvIGhpZ2h3YXlzLiBPbiB0aGUgb3RoZXIgaGFuZCwgdGhlIG1lYW4gYGxhZ0RldmVsb3BtZW50YCBpcyBzaWduaWZpY2FudGx5IGxvd2VyIGZvciB0aGUgYE5ldyBEZXZlbG9wbWVudGAgY2F0ZWdvcnkgY29tcGFyZWQgdG8gdGhlIGBObyBDaGFuZ2VgIGNhdGVnb3J5LCBpdCBtYXkgaW1wbHkgdGhhdCBuZXcgZGV2ZWxvcG1lbnQgaXMgbW9yZSBsaWtlbHkgdG8gb2NjdXIgaW4gYXJlYXMgdGhhdCBhcmUgbmVhciBleGlzdGluZyBkZXZlbG9wbWVudC4KCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KZGF0ICU+JQogIGRwbHlyOjpzZWxlY3QoZGlzdGFuY2VfaGlnaHdheXMsbGFnRGV2ZWxvcG1lbnQsbGNfY2hhbmdlKSAlPiUKICBnYXRoZXIoVmFyaWFibGUsIFZhbHVlLCAtbGNfY2hhbmdlLCAtZ2VvbWV0cnkpICU+JQogIGdncGxvdCguLCBhZXMobGNfY2hhbmdlLCBWYWx1ZSwgZmlsbD1sY19jaGFuZ2UpKSArIAogICAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiLCBzdGF0ID0gInN1bW1hcnkiLCBmdW4ueSA9ICJtZWFuIikgKwogICAgZmFjZXRfd3JhcCh+VmFyaWFibGUsIHNjYWxlcyA9ICJmcmVlX3kiKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwKICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJObyBDaGFuZ2UiLCJOZXcgRGV2ZWxvcG1lbnQiKSwKICAgICAgICAgICAgICAgICAgICAgIG5hbWU9IiIpICsKICAgIGxhYnModGl0bGU9Ik5ldyBEZXZlbG9wbWVudCBhcyBhIEZ1bmN0aW9uIG9mIHRoZSBDb250aW51b3VzIFZhcmlhYmxlcyIpICsKICAgIHBsb3RUaGVtZSAKYGBgCgpOZXh0LCB0aGUgc2FtZSB2aXN1YWxpemF0aW9uIGlzIGNyZWF0ZWQgZm9yIHRoZSBwb3B1bGF0aW9uIHJlbGF0ZWQgdmFyaWFibGVzLiBUaGUgaGlnaGVyIG1lYW4gdmFsdWVzIGZvciBgdG90YWxfcG9wdWxhdGlvbmAsIGB0b3RhbF9wb3B1bGF0aW9uMjAyMGAsIGFuZCBgcG9wX0NoYW5nZV8wMF8yMGAgaW4gdGhlIGBOZXcgRGV2ZWxvcG1lbnRgIGNhdGVnb3J5IHN1Z2dlc3QgdGhhdCBkZXZlbG9wbWVudCBpcyBtb3JlIGxpa2VseSB0byBvY2N1ciBpbiBhcmVhcyB3aXRoIGhpZ2hlciBwb3B1bGF0aW9ucyBhbmQgZ3JlYXRlciBwb3B1bGF0aW9uIGdyb3d0aC4gCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmRhdCAlPiUKICBkcGx5cjo6c2VsZWN0KHRvdGFsX3BvcHVsYXRpb24sdG90YWxfcG9wdWxhdGlvbjIwMjAscG9wX0NoYW5nZV8wMF8yMCxsY19jaGFuZ2UpICU+JQogIGdhdGhlcihWYXJpYWJsZSwgVmFsdWUsIC1sY19jaGFuZ2UsIC1nZW9tZXRyeSkgJT4lCiAgZ2dwbG90KC4sIGFlcyhsY19jaGFuZ2UsIFZhbHVlLCBmaWxsPWxjX2NoYW5nZSkpICsKICAgIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIiwgc3RhdCA9ICJzdW1tYXJ5IiwgZnVuLnkgPSAibWVhbiIpICsKICAgIGZhY2V0X3dyYXAoflZhcmlhYmxlKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwKICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJObyBDaGFuZ2UiLCJOZXcgRGV2ZWxvcG1lbnQiKSwKICAgICAgICAgICAgICAgICAgICAgIG5hbWU9IiIpICsKICAgIGxhYnModGl0bGU9Ik5ldyBEZXZlbG9wbWVudCBhcyBhIEZ1bmN0aW9uIG9mIEZhY3RvciBWYXJpYWJsZXMiKSArCiAgICBwbG90VGhlbWUKYGBgCgpOZXh0LCBhIHRhYmxlIG9mIGxhbmQgY292ZXIgY29udmVyc2lvbiBiZXR3ZWVuIDIwMDEgYW5kIDIwMTkgaXMgY3JlYXRlZC4gVGhlIHRhYmxlIHN1Z2dlc3RzIGZvciBpbnN0YW5jZSwgdGhhdCAwLjcxJSBvZiBUaGUgcGxvdHMgYWJvdmUgc3VnZ2VzdCB0aGF0IHRoZSBjb250aW51b3VzIHZhcmlhYmxlIChlLmcuLCBgZGlzdGFuY2VfaGlnaHdheXNgLCBgbGFnRGV2ZWxvcG1lbnRgLCBwb3B1bGF0aW9uIHJlbGF0ZWQgdmFyaWFibGVzKSBoYXMgYW4gYXNzb2NpYXRpb24gd2l0aCB0aGUgb2NjdXJyZW5jZSBvZiBuZXcgZGV2ZWxvcG1lbnQuCgoKTmV4dCwgYSB0YWJsZSBvZiBsYW5kIGNvdmVyIGNvbnZlcnNpb24gYmV0d2VlbiAyMDAxIGFuZCAyMDE5IGlzIGNyZWF0ZWQuIFRoZSB0YWJsZSBzdWdnZXN0cyBmb3IgaW5zdGFuY2UsIHRoYXQgMC43NyUgb2YgZmFybWxhbmQgcmVnaW9uYWxseSB3YXMgY29udmVydGVkIHRvIGRldmVsb3BtZW50IGJldHdlZW4gMjAwMSBhbmQgMjAxOS4gCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmRhdCAlPiUKICBkcGx5cjo6c2VsZWN0KGxjX2NoYW5nZTpvdGhlclVuZGV2ZWxvcGVkLGRldmVsb3BlZCkgJT4lCiAgZ2F0aGVyKExhbmRfQ292ZXJfVHlwZSwgVmFsdWUsIC1sY19jaGFuZ2UsIC1nZW9tZXRyeSkgJT4lCiAgIHN0X3NldF9nZW9tZXRyeShOVUxMKSAlPiUKICAgICBncm91cF9ieShsY19jaGFuZ2UsIExhbmRfQ292ZXJfVHlwZSkgJT4lCiAgICAgc3VtbWFyaXplKG4gPSBzdW0oYXMubnVtZXJpYyhWYWx1ZSkpKSAlPiUKICAgICB1bmdyb3VwKCkgJT4lCiAgICBtdXRhdGUoQ29udmVyc2lvbl9SYXRlID0gcGFzdGUwKHJvdW5kKDEwMCAqIG4vc3VtKG4pLCAyKSwgIiUiKSkgJT4lCiAgICBmaWx0ZXIobGNfY2hhbmdlID09IDEpICU+JQogIGRwbHlyOjpzZWxlY3QoTGFuZF9Db3Zlcl9UeXBlLENvbnZlcnNpb25fUmF0ZSkgJT4lCiAga2FibGUoKSAlPiUga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRikKYGBgCgoKIyA0LiBQcmVkaWN0aW5nIGZvciAyMDEwCgpJbiB0aGlzIHNlY3Rpb24sIHNpeCBzZXBhcmF0ZSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVscyBhcmUgZXN0aW1hdGVkIHRvIHByZWRpY3QgZGV2ZWxvcG1lbnQgY2hhbmdlIGJldHdlZW4gMjAwMSBhbmQgMjAxMSAtIHdpdGggZWFjaCBzdWJzZXF1ZW50IG1vZGVsIG1vcmUgc29waGlzdGljYXRlZCB0aGVuIHRoZSBsYXN0LiBUbyBkbyBzbywgdGhlIGRhdGEgaXMgc3BsaXQgaW50byA1MCUgdHJhaW5pbmcvdGVzdCBzZXRzLiBNb2RlbHMgYXJlIGVzdGltYXRlZCBvbiB0aGUgdHJhaW5pbmcgc2V0LgoKTm9ybWFsbHksIGFzIGluIHByZXZpb3VzIGNoYXB0ZXJzLCBhIHJlc3VsdHMgdGFibGUgcm93IHdvdWxkIGJlIGdlbmVyYXRlZCBmb3IgZWFjaCBtb2RlbCBkZXNjcmliaW5nIHRoZSBhY2N1cmFjeSBhbmQgZ2VuZXJhbGl6YWJpbGl0eSBvZiBwcmVkaWN0aW9ucyBmb3IgZWFjaCBzcGVjaWZpY2F0aW9uLiBGb3IgYnJldml0eSwgYSBsZXNzIHNvcGhpc3RpY2F0ZWQgYXBwcm9hY2ggaXMgdGFrZW4gaGVyZSwganVkZ2luZyBlYWNoIGJ5IHRoZSBNY0ZhZGRlbiBvciDigJxQc3VlZG/igJ0gUiBTcXVhcmVkIHN0YXRpc3RpYyBvbiB0aGUgdGVzdCBzZXQuIFRoZSBtb2RlbCB3aXRoIHRoZSBncmVhdGVzdCBnb29kbmVzcyBvZiBmaXQgaXMgdGhlbiB1c2VkIGZvciB0aGUgcHVycG9zZXMgb2YgcHJlZGljdGlvbi4KCiMjIDQuMi4gTW9kZWxpbmcKCkZpcnN0LCBgZGF0YCBpcyBzcGxpdCBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0IHNldHMuIE5vdGUgaG93IGltYmFsYW5jZWQgdGhlIHBhbmVsIGlzIHdpdGggYHRhYmxlKGRhdFRyYWluJGxjX2NoYW5nZTEpYC4KCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0Kc2V0LnNlZWQoMzQ1NikKdHJhaW5JbmRleCA8LSAKICBjcmVhdGVEYXRhUGFydGl0aW9uKGRhdCRkZXZlbG9wZWQsIHAgPSAuNTAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0ID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aW1lcyA9IDEpCmRhdFRyYWluIDwtIGRhdFsgdHJhaW5JbmRleCxdCmRhdFRlc3QgIDwtIGRhdFstdHJhaW5JbmRleCxdCgpucm93KGRhdCkKYGBgCgpOZXh0IHNpeCBzZXBhcmF0ZSBgZ2xtYCBtb2RlbHMgYXJlIGVzdGltYXRlZCBhZGRpbmcgbmV3IHZhcmlhYmxlcyBmb3IgZWFjaC4gRmlndXJlIDQuMSBzaG93cyB0aGUgUHN1ZWRvIFItU3F1YXJlZCBhc3NvY2lhdGVkIHdpdGggZWFjaCBtb2RlbC4KCmBNb2RlbDFgIGluY2x1ZGVzIG9ubHkgdGhlIDIwMDEgbGFuZCBjb3ZlciB0eXBlcy4gYE1vZGVsMmAgYWRkcyB0aGUgYGxhZ0RldmVsb3BtZW50YC4gTW9kZWxzIDMsIDQgYW5kIDUgYXR0ZW1wdCB0aHJlZSBkaWZmZXJlbnQgYXBwcm9hY2hlcyBmb3IgbW9kZWxpbmcgZGVtb2dyYXBoaWMgY2hhbmdlcywgaW5mcmFzdHJ1Y3R1cmUgYW5kIHNsb3BlLiBgTW9kZWwzYCB1c2VzIHBvcHVsYXRpb24gaW4gMjAwMCBhbmQgZGlzdGFuY2UgdG8gaGlnaHdheTsgYE1vZGVsNGAgYWRkcyAyMDIwIHNsb3BlIG9uIHRvcCBvZiBNb2RlbDM7IGBNb2RlbDVgIGFkZHMgcG9wdWxhdGlvbiBjaGFuZ2U7IGFuZCBgTW9kZWw2YCBhZGRzIGRlbW9ncmFwaGljIGZlYXR1cmVzLiBBbGwgYXJlIHNpZ25pZmljYW50IHNvIHdoaWNoIHBvcHVsYXRpb24gZmVhdHVyZSBzaG91bGQgYmUgY2hvc2VuPyBUaGUgYW5zd2VyIGxpZXMgaW4gaG93IHRoZSBtb2RlbCB3aWxsIGJlIHVzZWQgdG8gZm9yZWNhc3QuIAoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpNb2RlbDEgPC0gZ2xtKGxjX2NoYW5nZSB+IHdldGxhbmRzICsgZm9yZXN0ICArIGZhcm0gKyBvdGhlclVuZGV2ZWxvcGVkLCAKICAgICAgICAgICAgICBmYW1pbHk9ImJpbm9taWFsIihsaW5rPSJsb2dpdCIpLCBkYXRhID0gZGF0VHJhaW4pCgpNb2RlbDIgPC0gZ2xtKGxjX2NoYW5nZSB+IHdldGxhbmRzICsgZm9yZXN0ICArIGZhcm0gKyBvdGhlclVuZGV2ZWxvcGVkICsgbGFnRGV2ZWxvcG1lbnQsIAogICAgICAgICAgICAgIGZhbWlseT0iYmlub21pYWwiKGxpbms9ImxvZ2l0IiksIGRhdGEgPSBkYXRUcmFpbikKICAgICAgICAgICAgICAKTW9kZWwzIDwtIGdsbShsY19jaGFuZ2UgfiB3ZXRsYW5kcyArIGZvcmVzdCAgKyBmYXJtICsgb3RoZXJVbmRldmVsb3BlZCArIGxhZ0RldmVsb3BtZW50ICsgdG90YWxfcG9wdWxhdGlvbiArCiAgICAgICAgICAgICAgICAgZGlzdGFuY2VfaGlnaHdheXMsIAogICAgICAgICAgICAgIGZhbWlseT0iYmlub21pYWwiKGxpbms9ImxvZ2l0IiksIGRhdGEgPSBkYXRUcmFpbikgIAoKTW9kZWw0IDwtIGdsbShsY19jaGFuZ2UgfiB3ZXRsYW5kcyArIGZvcmVzdCAgKyBmYXJtICsgb3RoZXJVbmRldmVsb3BlZCArIGxhZ0RldmVsb3BtZW50ICsgdG90YWxfcG9wdWxhdGlvbiArCiAgICAgICAgICAgICAgICAgZGlzdGFuY2VfaGlnaHdheXMgKyBzbG9wZSwgCiAgICAgICAgICAgICAgICBmYW1pbHk9ImJpbm9taWFsIihsaW5rPSJsb2dpdCIpLCBkYXRhID0gZGF0VHJhaW4pICAKCk1vZGVsNSA8LSBnbG0obGNfY2hhbmdlIH4gd2V0bGFuZHMgKyBmb3Jlc3QgICsgZmFybSArIG90aGVyVW5kZXZlbG9wZWQgKyBsYWdEZXZlbG9wbWVudCArIHRvdGFsX3BvcHVsYXRpb24gKwogICAgICAgICAgICAgICAgIGRpc3RhbmNlX2hpZ2h3YXlzICsgc2xvcGUgKyBwb3BfQ2hhbmdlXzAwXzIwICsgdG90YWxfcG9wdWxhdGlvbjIwMjAsCiAgICAgICAgICAgICAgICBmYW1pbHk9ImJpbm9taWFsIihsaW5rPSJsb2dpdCIpLCBkYXRhID0gZGF0VHJhaW4pICAgICAgICAgICAgICAgCgpNb2RlbDYgPC0gZ2xtKGxjX2NoYW5nZSB+IHdldGxhbmRzICsgZm9yZXN0ICArIGZhcm0gKyBvdGhlclVuZGV2ZWxvcGVkICsgbGFnRGV2ZWxvcG1lbnQgKwogICAgICAgICAgICAgICAgIGRpc3RhbmNlX2hpZ2h3YXlzICsgc2xvcGUgKyBwb3BfQ2hhbmdlXzAwXzIwCiAgICAgICAgICAgICAgICAgKyB0b3RhbF9ob3VzaW5nX3VuaXRzICArIHRvdGFsX3doaXRlLAogICAgICAgICAgICAgICAgZmFtaWx5PSJiaW5vbWlhbCIobGluaz0ibG9naXQiKSwgZGF0YSA9IGRhdFRyYWluKQoKCmBgYAoKQmVsb3cgY29kZXMgY3JlYXRlIGEgZGF0YSBmcmFtZSBvZiBwc3VkZW8gUiBTcXVhcmVzIGZvciBlYWNoIG1vZGVsIGFuZCBwbG90dGluZyB0aGVtIGZvciBjb21wYXJpc29uLiBUaGlzIGFwcHJvYWNoIGxvb3BzIHRocm91Z2ggdGhlIG1vZGVscyByZXRyaWV2aW5nIHRoZSBnb29kbmVzcyBvZiBmaXQgZm9yIGVhY2guIGBNb2RlbDZgIGlzIHRoZSBmaW5hbCBtb2RlbCBlbXBsb3llZCBmb3IgcHJlZGljdGlvbi4KCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KbW9kZWxMaXN0IDwtIHBhc3RlMCgiTW9kZWwiLCAxOjYpCm1hcF9kZmMobW9kZWxMaXN0LCBmdW5jdGlvbih4KXBSMihnZXQoeCkpKVs0LF0gJT4lCiAgc2V0TmFtZXMocGFzdGUwKCJNb2RlbCIsMTo2KSkgJT4lCiAgZ2F0aGVyKE1vZGVsLE1jRmFkZGVuKSAlPiUKICBnZ3Bsb3QoYWVzKE1vZGVsLE1jRmFkZGVuKSkgKwogICAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSArCiAgICBsYWJzKHRpdGxlPSAiTWNGYWRkZW4gUi1TcXVhcmVkIGJ5IE1vZGVsIikgKwogICAgcGxvdFRoZW1lCmBgYAoKTmV4dCwgYSBkYXRhIGZyYW1lIGlzIGNyZWF0ZWQgdGhhdCBpbmNsdWRlcyBjb2x1bW5zIGZvciB0aGUgb2JzZXJ2ZWQgZGV2ZWxvcG1lbnQgY2hhbmdlLCBgbGNfY2hhbmdlYCwgYW5kIG9uZSB0aGF0IGluY2x1ZGVzIHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIGZvciBgTW9kZWw2YC4gVGhpcyBkYXRhIGZyYW1lIGlzIHRoZW4gdXNlZCBhcyBhbiBpbnB1dCB0byBhIGRlbnNpdHkgcGxvdCB2aXN1YWxpemluZyB0aGUgZGlzdHJpYnV0aW9uIG9mIHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIGJ5IG9ic2VydmVkIGNsYXNzLiBPbmx5IGEgc21hbGwgbnVtYmVyIG9mIHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIGFyZSBncmVhdGVyIHRoYW4gb3IgZXF1YWwgdG8gNTAlIGAobnJvdyhmaWx0ZXIodGVzdFNldFByb2JzLCBwcm9icyA+PSAuNTApKSAvIG5yb3coZGF0VGVzdCkpYC4gVGhpcyBtYWtlcyBnb29kIHNlbnNlLCBnaXZlbiBob3cgcmFyZSBvZiBhbiBldmVudCBkZXZlbG9wbWVudCBpcyBpbiBvdXIgZGF0YXNldC4gVWx0aW1hdGVseSwgaW4gb3JkZXIgdG8ganVkZ2Ugb3VyIG1vZGVsIHdpdGggYSBjb25mdXNpb24gbWF0cml4LCBhIHNtYWxsZXIgZGV2ZWxvcG1lbnQgY2xhc3NpZmljYXRpb24gdGhyZXNob2xkIG11c3QgYmUgZW1wbG95ZWQuCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CnRlc3RTZXRQcm9icyA8LSAKICBkYXRhLmZyYW1lKGNsYXNzID0gZGF0VGVzdCRsY19jaGFuZ2UsCiAgICAgICAgICAgICBwcm9icyA9IHByZWRpY3QoTW9kZWw2LCBkYXRUZXN0LCB0eXBlPSJyZXNwb25zZSIpKSAKICAKZ2dwbG90KHRlc3RTZXRQcm9icywgYWVzKHByb2JzKSkgKwogIGdlb21fZGVuc2l0eShhZXMoZmlsbD1jbGFzcyksIGFscGhhPTAuNSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUxLAogICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJObyBDaGFuZ2UiLCJOZXcgRGV2ZWxvcG1lbnQiKSkgKwogIGxhYnModGl0bGUgPSAiSGlzdG9ncmFtIG9mIHRlc3Qgc2V0IHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIiwKICAgICAgIHg9IlByZWRpY3RlZCBQcm9iYWJpbGl0aWVzIix5PSJEZW5zaXR5IikgKwogIHBsb3RUaGVtZQpgYGAKCiMjIDQuMy4gQWNjdXJhY3kKCk5vdyB0byBwaWNrIGEgcHJlZGljdGVkIHByb2JhYmlsaXR5IHRocmVzaG9sZCB0byBjbGFzc2lmeSBhbiBhcmVhIGFzIGhhdmluZyBuZXcgZGV2ZWxvcG1lbnQuICpTZW5zaXRpdml0eSogb3IgdGhlIFRydWUgUG9zaXRpdmUgcmF0ZSBpcyB0aGUgcHJvcG9ydGlvbiBvZiBhY3R1YWwgcG9zaXRpdmVzICgx4oCZcykgdGhhdCB3ZXJlIHByZWRpY3RlZCB0byBiZSBwb3NpdGl2ZS4gRm9yIGV4YW1wbGUsIHRoZSBTZW5zaXRpdml0eSBpbiBvdXIgbW9kZWwgaXMgdGhlIHJhdGUgb2YgZGV2ZWxvcGVkIGFyZWFzIGFjdHVhbGx5IHByZWRpY3RlZCBhcyBzdWNoLiAqU3BlY2lmaWNpdHkqIG9yIFRydWUgTmVnYXRpdmUgUmF0ZSBpcyB0aGUgcHJvcG9ydGlvbiBvZiBhY3R1YWwgbmVnYXRpdmVzICgw4oCZcykgdGhhdCB3ZXJlIHByZWRpY3RlZCB0byBiZSBuZWdhdGl2ZXMuIEZvciBleGFtcGxlLCB0aGUgU3BlY2lmaWNpdHkgaW4gb3VyIG1vZGVsIGlzIHRoZSByYXRlIG9mIE5vIENoYW5nZSBhcmVhcyB0aGF0IHdlcmUgY29ycmVjdGx5IHByZWRpY3RlZCBhcyBObyBjaGFuZ2UuCgpUaGVyZSBhcmUgc29tZSBjbGVhciB0cmFkZW9mZnMgYmV0d2VlbiBTZW5zaXRpdml0eSBhbmQgU3BlY2lmaWNpdHkgaW4gb3VyIG1vZGVsIHRoYXQgZGVzZXJ2ZSBzb21lIGV4cGxvcmF0aW9uLiBUbyBpbGx1c3RyYXRlLCB0d28gZGlmZmVyZW50IHRocmVzaG9sZHMgb2YgMTMlIGFuZCAzMCUgYXJlIGV4cGxvcmVkLiBQcmVkaWN0ZWQgY2xhc3NlcyBmb3IgYm90aCB0aHJlc2hvbGRzIGFyZSBnZW5lcmF0ZWQgYW5kIGluc3RlYWQgb2YgdXNpbmcgdGhlIGBjb25mdXNpb25NYXRyaXhgIGZ1bmN0aW9uIGZyb20gYGNhcmV0YCBhcyB3ZSBoYXZlIGluIHRoZSBwYXN0LCBoZXJlIGNvbmZ1c2lvbiBtYXRyaXggbWV0cmljcyBhcmUgZGVyaXZlZCBmcm9tIHRoZSBgeWFyZHN0aWNrYCBwYWNrYWdlLiBUaGlzIGFsbG93cyB1cyB0byBgZ3JvdXBfYnlgIHRoZSB0aHJlc2hvbGQgYW5kIGBzdW1tYXJpemVgIHRoZSBtZXRyaWNzIG9mIGludGVyZXN0LgoKVGhlIGBvcHRpb25zYCBjYWxsIGJlbG93IGlzIHJlcXVpcmVkIHRvIHRlbGwgYHlhcmRzdGlja2AgdGhhdCB0aGUgcG9zaXRpdmUgZmFjdG9yIGNsYXNzIGluIGB0ZXN0U2V0UHJvYnNgIGlzIGAxYC4gV2l0aG91dCBpdCwgeWFyZHN0aWNrIHdpbGwgYnkgZGVmYXVsdCwgc2VlIHRoZSBmaXJzdCBmYWN0b3IgbGV2ZWwgYXMgYDBgIGFuZCBmbGlwIHRoZSBjb25mdXNpb24gbWV0cmljcyBhcm91bmQuCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9Cm9wdGlvbnMoeWFyZHN0aWNrLmV2ZW50X2ZpcnN0ID0gRkFMU0UpCgp0ZXN0U2V0UHJvYnMgPC0gCiAgdGVzdFNldFByb2JzICU+JSAKICBtdXRhdGUocHJlZENsYXNzXzA1ID0gYXMuZmFjdG9yKGlmZWxzZSh0ZXN0U2V0UHJvYnMkcHJvYnMgPj0gMC4wNSAsMSwwKSksCiAgICAgICAgIHByZWRDbGFzc18yMCA9IGFzLmZhY3RvcihpZmVsc2UodGVzdFNldFByb2JzJHByb2JzID49IDAuMiAsMSwwKSkpIAoKdGVzdFNldFByb2JzICU+JQogIGRwbHlyOjpzZWxlY3QoLXByb2JzKSAlPiUKICBnYXRoZXIoVmFyaWFibGUsIFZhbHVlLCAtY2xhc3MpICU+JQogIGdyb3VwX2J5KFZhcmlhYmxlKSAlPiUKICBzdW1tYXJpemUoU2Vuc2l0aXZpdHkgPSByb3VuZCh5YXJkc3RpY2s6OnNlbnNfdmVjKGNsYXNzLGZhY3RvcihWYWx1ZSkpLDIpLAogICAgICAgICAgICBTcGVjaWZpY2l0eSA9IHJvdW5kKHlhcmRzdGljazo6c3BlY192ZWMoY2xhc3MsZmFjdG9yKFZhbHVlKSksMiksCiAgICAgICAgICAgIEFjY3VyYWN5ID0gcm91bmQoeWFyZHN0aWNrOjphY2N1cmFjeV92ZWMoY2xhc3MsZmFjdG9yKFZhbHVlKSksMikpICU+JSAKICBrYWJsZSgpICU+JQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEYpCmBgYAoKVGhlIDEzJSB0aHJlc2hvbGQgY29ycmVjdGx5IHByZWRpY3RzIGEgaGlnaGVyIG51bWJlciBvZiBuZXcgZGV2ZWxvcG1lbnQgYXJlYXMgKFNlbnNpdGl2aXR5KSwgYnV0IGluY29ycmVjdGx5IHByZWRpY3RzIGEgbG93ZXIgbnVtYmVyIG9mIG5vIGNoYW5nZSBhcmVhcyAoU3BlY2lmaWNpdHkpLiBBcyB0aGVyZSBhcmUgZmFyIG1vcmUgbm8gY2hhbmdlIGFyZWFzIGluIHRoZSBkYXRhLCB0aGlzIGlzIHJlZmxlY3RlZCBpbiBhIGxvd2VyIG92ZXJhbGwgYWNjdXJhY3kuIENvbnZlcnNlbHksIHRoZSAzMCUgdGhyZXNob2xkIGhhcyBhIGxvd2VyIFNlbnNpdGl2aXR5IHJhdGUgYW5kIGJ1dCBhIGZhciBoaWdoZXIgU3BlY2lmaWNpdHkgcmF0ZS4gQWdhaW4sIGJlY2F1c2Ugb2YgdGhlIGRhdGFzZXQgaXMgbWFqb3JpdHkgbm8gY2hhbmdlIGFyZWFzLCB0aGlzIGxlYWRzIHRvIGEgZmFyIGhpZ2hlciBBY2N1cmFjeSByYXRlLgoKR2l2ZW4gdGhlIHVzZSBjYXNlLCBhbmQgdGhlIHNwYXRpYWwgZGlzdHJpYnV0aW9uIG9mIGxhbmQgY292ZXIgY2hhbmdlLCBpdCBtYXkgYmUgbW9yZSB1c2VmdWwgdG8gaGF2ZSBhIG1vZGVsIHRoYXQgcHJlZGljdHMgZ2VuZXJhbGx5IHdoZXJlIG5ldyBkZXZlbG9wbWVudCBvY2N1cnMgcmF0aGVyIHRoYW4gb25lIHRoYXQgcHJlZGljdHMgcHJlY2lzZWx5IHdoZXJlLiBBcyBpbGx1c3RyYXRlZCBiZWxvdywgdGhlIDMwJSB0aHJlc2hvbGQgcHJvdmlkZXMgdGhpcyBvdXRjb21lLiBUaGVzZSB0cmFkZS1vZmZzIGNhbiBiZSB2aXN1YWxpemVkIGluIHRoZSBwbG90IGJlbG93LiBIZXJlIHRoZSBtb2RlbCBpcyB1c2VkIHRvIHByZWRpY3QgZm9yIHRoZSBlbnRpcmUgYGRhdGAgZGF0YXNldC4gMzAlIHRocmVzaG9sZCBsb29rcyBtb3JlIHJlYXNvbmFibGUgZ2l2ZW4gdGhlIGRpc3RyaWJ1dGlvbiBvZiBvYnNlcnZlZCBkZXZlbG9wbWVudCBjaGFuZ2UuCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CnByZWRzRm9yTWFwIDwtICAgICAgICAgCiAgZGF0ICU+JQogICAgbXV0YXRlKHByb2JzID0gcHJlZGljdChNb2RlbDYsIGRhdCwgdHlwZT0icmVzcG9uc2UiKSAsCiAgICAgICAgICAgVGhyZXNob2xkXzVfUGN0ID0gYXMuZmFjdG9yKGlmZWxzZShwcm9icyA+PSAwLjA1ICwxLDApKSwKICAgICAgICAgICBUaHJlc2hvbGRfMjBfUGN0ID0gIGFzLmZhY3RvcihpZmVsc2UocHJvYnMgPj0gMC4yMCAsMSwwKSkpICU+JQogICAgZHBseXI6OnNlbGVjdChsY19jaGFuZ2UsVGhyZXNob2xkXzVfUGN0LFRocmVzaG9sZF8yMF9QY3QpICU+JQogICAgZ2F0aGVyKFZhcmlhYmxlLFZhbHVlLCAtZ2VvbWV0cnkpICU+JQogICAgc3RfY2FzdCgiUE9MWUdPTiIpCmBgYAoKCjxkaXYgY2xhc3M9InN1cGVyYmlnaW1hZ2UiPgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlPSBGQUxTRSwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aD0gOH0KZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoZGF0YT1wcmVkc0Zvck1hcCwgYWVzKHg9eHlDKHByZWRzRm9yTWFwKVssMV0sIHk9eHlDKHByZWRzRm9yTWFwKVssMl0sIGNvbG91cj1WYWx1ZSkpICsKICBmYWNldF93cmFwKH5WYXJpYWJsZSkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTIsIGxhYmVscz1jKCJObyBDaGFuZ2UiLCJOZXcgRGV2ZWxvcG1lbnQiKSwKICAgICAgICAgICAgICAgICAgICAgIG5hbWU9IiIpICsKICBsYWJzKHRpdGxlPSJEZXZlbG9wbWVudCBQcmVkaWN0aW9ucyAtIExvdyBUaHJlc2hvbGQiKSArIAogIG1hcFRoZW1lCmBgYAo8L2Rpdj4KClRvIHByb3ZpZGUgYSBiaXQgbW9yZSBpbnNpZ2h0LCB0aGUgY29kZSBibG9jayBiZWxvdyBwcm9kdWNlcyBib3RoIHRydWUgcG9zaXRpdmVzIChTZW5zaXRpdml0eSkgYW5kIHRydWUgbmVnYXRpdmVzIChTcGVjaWZpY2l0eSkgZm9yIGVhY2ggZ3JpZCBjZWxsIGJ5IHRocmVzaG9sZCB0eXBlLiBOb3RpY2UgaG93IHRoZSBzcGF0aWFsIHBhdHRlcm4gb2YgU2Vuc2l0aXZpdHkgZm9yIGJvdGggdGhyZXNob2xkcyBpcyByZWxhdGl2ZWx5IGNvbnNpc3RlbnQsIGJ1dCB0aGUgMTMlIHRocmVzaG9sZCBtaXNzZXMgbW9zdCB0aGUgc3R1ZHkgYXJlYSB3aXRoIHJlc3BlY3QgdG8gU3BlY2lmaWNpdHkuCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CkNvbmZ1c2lvbk1hdHJpeC5tZXRyaWNzIDwtCiAgZGF0ICU+JQogICAgbXV0YXRlKHByb2JzID0gcHJlZGljdChNb2RlbDMsIGRhdCwgdHlwZT0icmVzcG9uc2UiKSAsCiAgICAgICAgICAgVGhyZXNob2xkXzVfUGN0ID0gYXMuZmFjdG9yKGlmZWxzZShwcm9icyA+PSAwLjA1ICwxLDApKSwKICAgICAgICAgICBUaHJlc2hvbGRfMjBfUGN0ID0gIGFzLmZhY3RvcihpZmVsc2UocHJvYnMgPj0gMC4yMCAsMSwwKSkpICU+JQogICAgbXV0YXRlKFRydWVQXzA1ID0gaWZlbHNlKGxjX2NoYW5nZSAgPT0gMSAmIFRocmVzaG9sZF81X1BjdCA9PSAxLCAxLDApLAogICAgICAgICAgIFRydWVOXzA1ID0gaWZlbHNlKGxjX2NoYW5nZSAgPT0gMCAmIFRocmVzaG9sZF81X1BjdCA9PSAwLCAxLDApLAogICAgICAgICAgIFRydWVQXzIwID0gaWZlbHNlKGxjX2NoYW5nZSAgPT0gMSAmIFRocmVzaG9sZF8yMF9QY3QgPT0gMSwgMSwwKSwKICAgICAgICAgICBUcnVlTl8yMCA9IGlmZWxzZShsY19jaGFuZ2UgID09IDAgJiBUaHJlc2hvbGRfMjBfUGN0ID09IDAsIDEsMCkpICU+JQogICAgZHBseXI6OnNlbGVjdCguLCBzdGFydHNfd2l0aCgiVHJ1ZSIpKSAlPiUKICAgIGdhdGhlcihWYXJpYWJsZSwgVmFsdWUsIC1nZW9tZXRyeSkgJT4lCiAgICBzdF9jYXN0KCJQT0xZR09OIikgCmBgYAoKPGRpdiBjbGFzcz0ic3VwZXJiaWdpbWFnZSI+CmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmhlaWdodD0gOCwgZmlnLndpZHRoPSA4IH0KZ2dwbG90KGRhdGE9Q29uZnVzaW9uTWF0cml4Lm1ldHJpY3MpICsKICBnZW9tX3BvaW50KGFlcyh4PXh5QyhDb25mdXNpb25NYXRyaXgubWV0cmljcylbLDFdLCAKICAgICAgICAgICAgICAgICB5PXh5QyhDb25mdXNpb25NYXRyaXgubWV0cmljcylbLDJdLCBjb2xvdXIgPSBhcy5mYWN0b3IoVmFsdWUpKSkgKwogIGZhY2V0X3dyYXAoflZhcmlhYmxlKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwgbGFiZWxzPWMoIkNvcnJlY3QiLCJJbmNvcnJlY3QiKSwKICAgICAgICAgICAgICAgICAgICAgICBuYW1lPSIiKSArCiAgbGFicyh0aXRsZT0iRGV2ZWxvcG1lbnQgUHJlZGljdGlvbnMgLSBMb3cgVGhyZXNob2xkIikgKyBtYXBUaGVtZQpgYGAKPC9kaXY+CgoKCiMgNS4gUHJlZGljdGluZyBMYW5kIENvdmVyIERlbWFuZCBmb3IgMjA0MAoKQXQgdGhpcyBwb2ludCwgYSBzaW1wbGUgYnV0IHVzZWZ1bCBtb2RlbCBoYXMgYmVlbiB0cmFpbmVkIHRvIHByZWRpY3QgdXJiYW4gZGV2ZWxvcG1lbnQgYmV0d2VlbiAyMDAxIGFuZCAyMDE5IGFzIGEgZnVuY3Rpb24gb2YgYmFzZWxpbmUgZmVhdHVyZXMgZnJvbSAyMDAxIGluY2x1ZGluZyBsYW5kIGNvdmVyLCBidWlsdCBlbnZpcm9ubWVudCBhbmQgcG9wdWxhdGlvbi4gTmV4dCwgd2UgYXJlIGdvaW5nIHRvIHVwZGF0ZSBvdXIgZmVhdHVyZXMgdG8gcmVmbGVjdCBhIDIwMTkgYmFzZWxpbmUuIEhhdmluZyBkb25lIHNvLCBwcmVkaWN0aW9ucyBmcm9tIG91ciBuZXcgbW9kZWwgd291bGQgdGhlbiBiZSBmb3IgMjA0MC4KCkZvciBicmV2aXR5LCB3ZSBvbmx5IHVwZGF0ZSB0d28gZmVhdHVyZXMgaW4gb3VyIG1vZGVsIGZvciBub3cuIEZpcnN0LCBwb3B1bGF0aW9uIGNoYW5nZSAoYHBvcF9jaGFuZ2VgKSBpcyB1cGRhdGVkIHVzaW5nIGNvdW50eSBsZXZlbCBwb3B1bGF0aW9uIHByb2plY3Rpb25zIHZpc3VhbGl6ZWQgaW4gdGhlIHBsb3QgYmVsb3cuIFRoZSBzZWNvbmQgaXMgYGxhZ0RldmVsb3BtZW50YCwgd2hpY2ggZGVzY3JpYmVzIGhvdyBwcmVkaWN0ZWQgbmV3IGRldmVsb3BtZW50IHJlbGF0ZXMgaW4gc3BhY2UgdG8gb2xkIGRldmVsb3BtZW50LgoKT25jZSB0aGUgZmVhdHVyZXMgYXJlIHVwZGF0ZWQsIDIwNDAgcHJlZGljdGlvbnMgYXJlIGVzdGltYXRlZCBhbmQgbWFwcGVkLgoKQmVsb3csIGBsYWdEZXZlbG9wbWVudGAgaXMgbXV0YXRlIGRlc2NyaWJpbmcgYXZlcmFnZSBkaXN0YW5jZSB0byAyMDE5IGRldmVsb3BtZW50LiBOb3RlIHRoYXQgdGhlIGZpZWxkIG5hbWUsIGBsYWdEZXZlbG9wbWVudGAgaXMgdW5jaGFuZ2VkIChpZS4gbm90IHVwZGF0ZWQgdG8gYGxhZ0RldmVsb3BtZW50XzIwMTlgKS4gVGhpcyBpcyBkb25lIHB1cnBvc2VmdWxseSBhcyBtb2RlbDYgaGFzIGEgcmVncmVzc2lvbiBjb2VmZmljaWVudCBjYWxsZWQgYGxhZ0RldmVsb3BtZW50YC4gSWYgdGhpcyB2YXJpYWJsZSB3YXNu4oCZdCBwcmVzZW50IGluIG91ciB1cGRhdGVkIGRhdGEgZnJhbWUgdGhlbiB0aGUgYHByZWRpY3RgIGNvbW1hbmQgd291bGQgZmFpbC4KCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KZGF0IDwtCiAgZGF0ICU+JQogIG11dGF0ZShsYWdEZXZlbG9wbWVudCA9IG5uX2Z1bmN0aW9uKHh5QyguKSwgeHlDKGZpbHRlciguLGRldmVsb3BlZDEwID09IDIpKSwyKSkKYGBgCgpOb3cgdG8gdXBkYXRlIHBvcHVsYXRpb24gY2hhbmdlLiBBIG5ldyBkYXRhIGZyYW1lLCBgY291bnR5UG9wdWxhdGlvbl8yMDQwYCBpcyBjcmVhdGVkIHdoaWNoIGluY2x1ZGVzIDIwMjAgcG9wdWxhdGlvbiBjb3VudHMgYW5kIDIwNDAgcHJvamVjdGlvbnMgZm9yIGVhY2ggY291bnR5IGluIHRoZSBzdHVkeSBhcmVhLiBQb3B1bGF0aW9uIGlzIHBsb3R0ZWQgYnkgeWVhciBhbmQgYnkgY291bnR5LiAKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KY291bnR5UG9wdWxhdGlvbl8yMDQwIDwtIAogIGRhdGEuZnJhbWUoCiAgIE5BTUUgPSAKICAgICBjKCJBbGJlbWFybGUiLCJDaGFybG90dGVzdmlsbGUiKSwKICAgY291bnR5X3Byb2plY3Rpb25fMjA0MCA9IAogICAgIGMoMTM4NTIzLDQ4OTM5KSkgJT4lCiAgIGxlZnRfam9pbigKICAgICBkYXQgJT4lCiAgICAgICBzdF9zZXRfZ2VvbWV0cnkoTlVMTCkgJT4lCiAgICAgICBncm91cF9ieShOQU1FKSAlPiUKICAgICAgIHN1bW1hcml6ZShjb3VudHlfcG9wdWxhdGlvbl8yMDIwID0gcm91bmQoc3VtKHRvdGFsX3BvcHVsYXRpb24yMDIwKSkpKQoKY291bnR5UG9wdWxhdGlvbl8yMDQwICU+JQogIGdhdGhlcihWYXJpYWJsZSxWYWx1ZSwgLU5BTUUpICU+JQogIGdncGxvdChhZXMocmVvcmRlcihOQU1FLC1WYWx1ZSksVmFsdWUpKSArCiAgZ2VvbV9iYXIoYWVzKGZpbGw9VmFyaWFibGUpLCBzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTIsCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIjIwNDAiLCIyMDIwIiksCiAgICAgICAgICAgICAgICAgICAgbmFtZT0iUG9wdWxhdGlvbiIpICsKICBsYWJzKHRpdGxlPSJQb3B1bGF0aW9uIENoYW5nZSBieSBDb3VudHk6IDIwMjAgLSAyMDQwIiwKICAgICAgIHg9IkNvdW50eSIsIHk9IlBvcHVsYXRpb24iKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgKwogIHBsb3RUaGVtZQpgYGAKCiMjIDUuMi4gUHJlZGljdGluZyBEZXZlbG9wbWVudCBEZW1hbmQKCk5leHQsIHRoZSBgY291bnR5UG9wdWxhdGlvbl8yMDQwYCB0YWJsZSBpcyBqb2luZWQgdG8gYGRhdGAgYW5kIGBwb3BfY2hhbmdlYCBpbiBvcmRlciB0byDigJhkaXN0cmlidXRl4oCZIHRoZSBuZXcgcG9wdWxhdGlvbiBhY3Jvc3MgdGhlIHN0dWR5IGFyZWEuIFRvIGRvIHNvLCB0aGUgdGhlIGFsbG9jYXRpb24gb2YgbmV3IHBvcHVsYXRpb24gaXMgd2VpZ2h0ZWQgYnkgYSBncmlkIGNlbGzigJlzIGV4aXN0aW5nIHBvcHVsYXRpb24gKGBwb3BfMjA0MC5pbmZpbGxgKS4gMjAyMCBwb3B1bGF0aW9uIGlzIHN1YnRyYWN0ZWQgZnJvbSB0aGlzIGZpZ3VyZSB0byBnZXQgYHBvcF9DaGFuZ2VgLiBGaW5hbGx5LCBgTW9kZWw2YCBpcyB1c2VkIHRvIHByZWRpY3QgZm9yIDIwNDAgZ2l2ZW4gdGhlIHVwZGF0ZWQgcG9wdWxhdGlvbiBjaGFuZ2UgYW5kIGxhZyBkZXZlbG9wbWVudCBmZWF0dXJlcy4KClRoZSBtYXAgb2YgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMgdGhhdCByZXN1bHRzIGlzIGJlc3QgdGhvdWdodCBvZiBhcyBhIG1lYXN1cmUgb2YgcHJlZGljdGVkIGRldmVsb3BtZW50IGRlbWFuZCBpbiAyMDQwLgoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpkYXRfaW5maWxsIDwtCiAgZGF0ICU+JQogICNjYWxjdWxhdGUgcG9wdWxhdGlvbiBjaGFuZ2UKICAgIGxlZnRfam9pbihjb3VudHlQb3B1bGF0aW9uXzIwNDApICU+JQogICAgbXV0YXRlKHByb3BvcnRpb25fb2ZfY291bnR5X3BvcCA9IHRvdGFsX3BvcHVsYXRpb24yMDIwIC8gY291bnR5X3BvcHVsYXRpb25fMjAyMCwKICAgICAgICAgICBwb3BfMjA0MC5pbmZpbGwgPSBwcm9wb3J0aW9uX29mX2NvdW50eV9wb3AgKiBjb3VudHlfcHJvamVjdGlvbl8yMDQwLAogICAgICAgICAgIHBvcF9DaGFuZ2UgPSByb3VuZChwb3BfMjA0MC5pbmZpbGwgLSB0b3RhbF9wb3B1bGF0aW9uMjAyMCksMikgJT4lCiAgICBkcGx5cjo6c2VsZWN0KC1jb3VudHlfcHJvamVjdGlvbl8yMDQwLCAtY291bnR5X3BvcHVsYXRpb25fMjAyMCwgCiAgICAgICAgICAgICAgICAgIC1wcm9wb3J0aW9uX29mX2NvdW50eV9wb3AsIC1wb3BfMjA0MC5pbmZpbGwpICU+JQogICNhZGQgdmFsdWVzIGZvciAyMDIwIGJhc2VsaW5lIChmb3Jlc3QsIGZhcm0sIGV0Yy4pCiAgCiAgI3ByZWRpY3QgZm9yIDIwNDAKICAgIG11dGF0ZShwcmVkaWN0XzIwNDAuaW5maWxsID0gcHJlZGljdChNb2RlbDMsLiAsIHR5cGU9InJlc3BvbnNlIikpCgojIGRhdF9pbmZpbGwgPC0gZGF0X2luZmlsbCAlPiUgCiMgICBtdXRhdGUocHJlZGljdF8yMDQwLmluZmlsbDIgPWlmZWxzZShwcmVkaWN0XzIwNDAuaW5maWxsIDwgMC4wMSB8IHByZWRpY3RfMjA0MC5pbmZpbGwgPiAxLCAwLCBwcmVkaWN0XzIwNDAuaW5maWxsKSkKCmRhdF9pbmZpbGwgJT4lCiAgZ2dwbG90KCkgKyAgCiAgZ2VvbV9zZihhZXMoZmlsbCA9IHByZWRpY3RfMjA0MC5pbmZpbGwpLCBjb2xvciA9ICJ0cmFuc3BhcmVudCIpICsKICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9IHBhbGV0dGU1WzFdLAogICAgICAgICAgICAgICAgICAgICAgaGlnaCA9IHBhbGV0dGU1W2xlbmd0aChwYWxldHRlNSldLAogICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJTdHJldGNoZWRcblZhbHVlcyIpICsKICBnZW9tX3NmKGRhdGEgPSBzdHVkeUFyZWFDb3VudGllcywgZmlsbCA9IE5BLCBjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMSkgKwogIGxhYnModGl0bGUgPSAiRGV2ZWxvcG1lbnQgRGVtYW5kIGluIDIwNDA6IFByZWRpY3RlZCBQcm9iYWJpbGl0aWVzIikgKwogIG1hcFRoZW1lCgpgYGAKCiMgNi4gQ29tcGFyaW5nIFByZWRpY3RlZCBEZXZlbG9wbWVudCBEZW1hbmQgJiBFbnZpcm9ubWVudGFsIFNlbnNpdGl2aXR5CgpXZSBub3cgaGF2ZSBhIHJlYWxseSBzdHJvbmcgaW5kaWNhdG9yIG9mIGRldmVsb3BtZW50IGRlbWFuZCBmb3IgMjAyMCB0byBoZWxwIGd1aWRlIGxvY2FsIGxhbmQgdXNlIHBsYW5uaW5nLiBEZW1hbmQgaG93ZXZlciwgaXMgb25seSBvbmUgc2lkZSBvZiB0aGUgZXF1YXRpb24uIEl0IG11c3QgYmFsYW5jZWQgd2l0aCB0aGUgc3VwcGx5IG9mIGVudmlyb25tZW50YWxseSBzZW5zaXRpdmUgbGFuZC4gVW5kZXJzdGFuZGluZyB0aGUgaW50ZXJwbGF5IGJldHdlZW4gZGVtYW5kIGFuZCBzdXBwbHkgaXMgdGhlIGZpcnN0IHN0YWdlIG9mIHRoZSDigJhBbGxvY2F0aW9u4oCZIHBoYXNlLCB3aGVyZSBQbGFubmVycyB1bHRpbWF0ZWx5IGRlY2lkZSB3aGljaCBsYW5kIHNob3VsZCBiZSBkZXZlbG9wZWQgYW5kIHdoaWNoIHNob3VsZCBub3QuCgpGb3IgdGhpcyBhbmFseXNpcyBmYXJtbGFuZCBhbmQgdW5kZXZlbG9wZWQgbGFuZCBhcmUgYmUgZGVlbWVkIGBTdWl0YWJsZWAsIHdoaWxlIGVudmlyb25tZW50YWxseSBzZW5zaXRpdmUgYXJlYXMgbGlrZSB3ZXRsYW5kcyBhbmQgZm9yZXN0IGFyZSBiZSBkZWVtZWQgYE5vdCBTdWl0YWJsZWAuIEJlbG93LCAyMDE5IGxhbmQgY292ZXIgZGF0YSBpcyByZWFkIGluIGFuZCBzZXZlcmFsIG1lYXN1cmVzIG9mIGVudmlyb25tZW50YWwgc2Vuc2l0aXZpdHkgYXJlIGNyZWF0ZWQgYnkgY291bnR5LiBUaGVzZSBpbmNsdWRlOgoKMS4gVGhlIHRvdGFsIGFtb3VudCBvZiB3ZXRsYW5kcyBhbmQgZm9yZXN0IGxhbmQgY292ZXIgYXJlYSBpbiAyMDE5LgoyLiBUaGUgYW1vdW50IG9mIHNlbnNpdGl2ZSBsYW5kICh3ZXRsYW5kIGFuZCBmb3Jlc3QpIGxvc3QgYmV0d2VlbiAyMDAxIGFuZCAyMDE5LgozLiBUaGUgdG90YWwgYXJlYSBvZiBsYXJnZSBzZW5zaXRpdmUgbGFuZHNjYXBlIOKAmHBhdGNoZXPigJkgaW4gMjAxOS4KClRoZSB0aGlyZCBtZXRyaWMgd2FycmFudHMgc29tZSBmdXJ0aGVyIGRpc2N1c3Npb24uIEluIHRoZSBjb250ZXh0IG9mIGxlYXBmcm9nIGRldmVsb3BtZW50LCBTZWN0aW9uIDIuNiBkaXNjdXNzZXMgdGhlIGNvbmNlcHQgb2YgbGFuZHNjYXBlIGZyYWdtZW50YXRpb24gLSB0aGUgaWRlYSB0aGF0IGRpc2NvbnRpbnVvdXMgZGV2ZWxvcG1lbnQgYWNyb3NzIHNwYWNlIGNhcnZlcyBvdXQgZGlzam9pbnRlZCBzbGl2ZXJzIG9mIHdpbGRlcm5lc3MuIFRoaXMgZnJhZ21lbnRhdGlvbiByZWR1Y2VzIGJpb2RpdmVyc2l0eSBwYXJ0aWN1bGFybHkgZm9yIHNwZWNpZXMgdGhhdCBuZWVkIHJvb20gdG8gcm9hbS4gQmVsb3csIGVudmlyb25tZW50YWxseSBgc2Vuc2l0aXZlX3JlZ2lvbnNgIGFyZSBjcmVhdGVkIHRvIHJlcHJlc2VudCBsYXJnZSBhcmVhcyBvZiB1bmZyYWdtZW50ZWQgbmF0dXJhbCByZXNvdXJjZXMuIFdlIHRoZW4gY29uc2lkZXIgdGhlIHRvdGFsIGFyZWEgb2YgdGhlc2UgY2x1bXBzIGZvciBlYWNoIGNvdW50eS4KCiMjIDYuMi4gMjAxMSBMYW5kIENvdmVyIERhdGEKClRvIGJlZ2luLCB0aGUgMjAxOSBMYW5kIENvdmVyIGRhdGEgaXMgcmVhZCBpbiBhbmQgcmVjbGFzc2lmaWVkLgoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQojIFJlYWQgcmFzdGVyIGRhdGEKbGNfMjAxOSA8LSByYXN0ZXIoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9ZaW5ndG9uZy1aL3NwcmF3bC1mb3JlY2FzdGluZy9iNTI4ZDcyNWVhZWE1ZGQ3NzhjNDFmYTY0NGI1ZTZmNzFkNjI5MWIyL2RhdGEvQ3ZpbGxlX0xDXzIwMTkudGlmIikKCgpnZ3Bsb3QoKSArCiAgZ2VvbV9yYXN0ZXIoZGF0YSA9IHJiaW5kKHJhc3QobGNfMjAwMSkgJT4lIG11dGF0ZShsYWJlbCA9ICIyMDAxIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhc3QobGNfMjAxOSkgJT4lIG11dGF0ZShsYWJlbCA9ICIyMDE5IikpICU+JSAKICAgICAgICAgICAgICBuYS5vbWl0ICU+JSBmaWx0ZXIodmFsdWUgPiAwKSwgCiAgICAgICAgICAgICAgYWVzKHgseSxmaWxsPWFzLmZhY3Rvcih2YWx1ZSkpKSArCiAgZ2VvbV9zZihkYXRhID0gc3R1ZHlBcmVhQ291bnRpZXMsIGZpbGwgPSBOQSwgY29sb3VyID0gInJlZCIsIHNpemUgPSAxKSArCiAgZmFjZXRfd3JhcCh+bGFiZWwpICsKICBzY2FsZV9maWxsX3ZpcmlkaXMoZGlzY3JldGU9VFJVRSwgbmFtZSA9IiIpICsKICBsYWJzKHRpdGxlID0gIkxhbmQgQ292ZXIsIDIwMDEgJiAyMDE5IikgKwogIG1hcFRoZW1lICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCk5leHQsIGVhY2ggcmFzdGVyIGlzIGFnZ3JlZ2F0ZWQgdG8gdGhlIGZpc2huZXQgdXNpbmcgdGhlIGBhZ2dyZWdhdGVSYXN0ZXJgIGZ1bmN0aW9uIGFuZCAyMDE5IGxhbmQgY292ZXIgdHlwZXMgYXJlIG1hcHBlZC4KCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KbGNfMjAxOSA8LSByYXN0ZXIoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9ZaW5ndG9uZy1aL3NwcmF3bC1mb3JlY2FzdGluZy9iNTI4ZDcyNWVhZWE1ZGQ3NzhjNDFmYTY0NGI1ZTZmNzFkNjI5MWIyL2RhdGEvQ3ZpbGxlX0xDXzIwMTkudGlmIikKCmRldmVsb3BlZDE5IDwtIGxjXzIwMTkgPT0gMjEgfCBsY18yMDE5ID09IDIyIHwgbGNfMjAxOSA9PSAyMyB8IGxjXzIwMTkgPT0gMjQKZm9yZXN0MTkgPC0gbGNfMjAxOSA9PSA0MSB8IGxjXzIwMTkgPT0gNDIgfCBsY18yMDE5ID09IDQzCmZhcm0xOSA8LSBsY18yMDE5ID09IDgxIHwgbGNfMjAxOSA9PSA4Mgp3ZXRsYW5kczE5IDwtIGxjXzIwMTkgPT0gOTAgfCBsY18yMDE5ID09IDk1Cm90aGVyVW5kZXZlbG9wZWQxOSA8LSBsY18yMDE5ID09IDUyIHwgbGNfMjAxOSA9PSA3MSB8IGxjXzIwMTkgPT0gMzEKd2F0ZXIxOSA8LSBsY18yMDE5ID09IDExCgpuYW1lcyhkZXZlbG9wZWQxOSkgPC0gImRldmVsb3BlZDE5IgpuYW1lcyhmb3Jlc3QxOSkgPC0gImZvcmVzdDE5IgpuYW1lcyhmYXJtMTkpIDwtICJmYXJtMTkiCm5hbWVzKHdldGxhbmRzMTkpIDwtICJ3ZXRsYW5kczE5IgpuYW1lcyhvdGhlclVuZGV2ZWxvcGVkMTkpIDwtICJvdGhlclVuZGV2ZWxvcGVkMTkiCm5hbWVzKHdhdGVyMTkpIDwtICJ3YXRlcjE5IgoKdGhlUmFzdGVyTGlzdDE5IDwtIGMoZGV2ZWxvcGVkMTksZm9yZXN0MTksZmFybTE5LHdldGxhbmRzMTksb3RoZXJVbmRldmVsb3BlZDE5LHdhdGVyMTkpCgojIyNhc3NpZ24gbGMgdmFsdWVzIGJhc2VkIG9uIGhpZXJhcmNoeQpkYXQyIDwtCiAgYWdncmVnYXRlUmFzdGVyKHRoZVJhc3Rlckxpc3QxOSwgZGF0X2luZmlsbCkgJT4lCiAgZHBseXI6OnNlbGVjdChkZXZlbG9wZWQxOSxmb3Jlc3QxOSxmYXJtMTksd2V0bGFuZHMxOSxvdGhlclVuZGV2ZWxvcGVkMTksd2F0ZXIxOSkgJT4lCiAgc3Rfc2V0X2dlb21ldHJ5KE5VTEwpICU+JQogIGJpbmRfY29scyguLGRhdCkgJT4lCiAgc3Rfc2YoKSAlPiUKICBzdF9jYXN0KCJQT0xZR09OIikKIyByZWFzc2lnbiBjZWxscyB3aXRoIG1vcmUgdGhhbiBvbmUgbGMgdmFsdWUgdG8ganVzdCBvbmUgYmFzZWQgb24gaGllcmFyY2h5IHNob3duIGhlcmUKZGF0MiA8LSBkYXQyICU+JQogICAgbXV0YXRlKGZhcm0xOSA9IGlmZWxzZShkZXZlbG9wZWQxOSA9PSAwICYgZmFybTE5ID09IDEsIDEsIDApKSAlPiUKICAgIG11dGF0ZShmb3Jlc3QxOSA9IGlmZWxzZShkZXZlbG9wZWQxOSA9PSAwICYgZmFybTE5ID09IDAgJiBmb3Jlc3QxOSA9PSAxLCAxLCAwKSkgJT4lCiAgICBtdXRhdGUod2V0bGFuZHMxOSA9IGlmZWxzZShkZXZlbG9wZWQxOSA9PSAwICYgZmFybTE5ID09IDAgJiBmb3Jlc3QxOSA9PSAwICYgd2V0bGFuZHMxOSA9PSAxLCAxLCAwKSkgJT4lCiAgICBtdXRhdGUod2F0ZXIxOSA9IGlmZWxzZShkZXZlbG9wZWQxOSA9PSAwICYgZmFybTE5ID09IDAgJiBmb3Jlc3QxOSA9PSAwICYgd2V0bGFuZHMxOSA9PSAwICYgd2F0ZXIxOSA9PSAxLCAxLCAwKSkgJT4lCiAgICBtdXRhdGUob3RoZXJVbmRldmVsb3BlZDE5ID0gaWZlbHNlKGRldmVsb3BlZDE5ID09IDAgJiBmYXJtMTkgPT0gMCAmIGZvcmVzdDE5ID09IDAgJiB3ZXRsYW5kczE5ID09IDAgJiB3YXRlcjE5ID09IDAgJiBvdGhlclVuZGV2ZWxvcGVkMTkgPT0gMSwgMSwgMCkpCgojICMjI2Fzc2lnbiBsYyB2YWx1ZXMgYmFzZWQgb24gbW9zdCBjb21tb24gbGMgdHlwZSB3aXRoaW4gZWFjaCBmaXNobmV0IGNlbGwKIyBkYXQyIDwtCiMgICBhZ2dyZWdhdGVSYXN0ZXIodGhlUmFzdGVyTGlzdDE5LCBkYXRfaW5maWxsKSAlPiUKIyAgIGRwbHlyOjpzZWxlY3QoZGV2ZWxvcGVkMTksZm9yZXN0MTksZmFybTE5LHdldGxhbmRzMTksb3RoZXJVbmRldmVsb3BlZDE5LHdhdGVyMTkpICU+JQojICAgc3Rfc2V0X2dlb21ldHJ5KE5VTEwpICU+JQojICAgbXV0YXRlKGFjcm9zcyhldmVyeXRoaW5nKCksIGFzLm51bWVyaWMpKSAlPiUgIyBDb252ZXJ0IGFsbCBjb2x1bW5zIHRvIG51bWVyaWMKIyAgIG11dGF0ZShtYXhfY29sID0gYXBwbHkoLiwgMSwgd2hpY2gubWF4KSkgJT4lICMgRmluZCB0aGUgaW5kZXggb2YgdGhlIG1heGltdW0gdmFsdWUgaW4gZWFjaCByb3cKIyAgIG11dGF0ZShhY3Jvc3MoZXZlcnl0aGluZygpLCB+cmVwbGFjZSguLCByb3dfbnVtYmVyKCkgIT0gbWF4X2NvbCwgMCksIC5uYW1lcyA9ICJuZXdfey5jb2x9IikpICU+JSAjIFJlcGxhY2Ugbm9uLW1heGltdW0gdmFsdWVzIHdpdGggMAojICAgZHBseXI6OnNlbGVjdChuZXdfZGV2ZWxvcGVkMTk6bmV3X3dhdGVyMTkpICU+JQojICAgYmluZF9jb2xzKC4sZGF0KSAlPiUKIyAgIHN0X3NmKCkgJT4lCiMgICBzdF9jYXN0KCJQT0xZR09OIikKIyAjcmVuYW1lIGNvbHVtbnMgdG8gbWF4IG5hbWVzCiMgZGF0MiA8LSBkYXQyICU+JQojICAgbXV0YXRlKGRldmVsb3BlZDE5ID0gbmV3X2RldmVsb3BlZDE5KSAlPiUKIyAgIG11dGF0ZShmb3Jlc3QxOSA9IG5ld19mb3Jlc3QxOSkgJT4lCiMgICBtdXRhdGUoZmFybTE5ID0gbmV3X2Zhcm0xOSkgJT4lCiMgICBtdXRhdGUod2V0bGFuZHMxOSA9IG5ld193ZXRsYW5kczE5KSAlPiUKIyAgIG11dGF0ZShvdGhlclVuZGV2ZWxvcGVkMTkgPSBuZXdfb3RoZXJVbmRldmVsb3BlZDE5KSAlPiUKIyAgIG11dGF0ZSh3YXRlcjE5ID0gbmV3X3dhdGVyMTkpCgoKZGF0MiAlPiUKICBnYXRoZXIodmFyLHZhbHVlLGRldmVsb3BlZDE5OndhdGVyMTkpICU+JQogIHN0X2NlbnRyb2lkKCkgJT4lCiAgbXV0YXRlKFggPSBzdF9jb29yZGluYXRlcyguKVssMV0sCiAgICAgICAgIFkgPSBzdF9jb29yZGluYXRlcyguKVssMl0pICU+JQogIGdncGxvdCgpICsKICAgIGdlb21fc2YoZGF0YT1DdmlsbGVNU0EpICsKICAgIGdlb21fcG9pbnQoYWVzKFgsWSwgY29sb3VyPWFzLmZhY3Rvcih2YWx1ZSkpLCBzaXplID0gMC4xKSArCiAgICBmYWNldF93cmFwKH52YXIpICsKICAgIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTIsCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJPdGhlciIsIkxhbmQgQ292ZXIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICIiKSArCiAgICBsYWJzKHRpdGxlID0gIkxhbmQgQ292ZXIgVHlwZXMsIDIwMTkiLAogICAgICAgICBzdWJ0aXRsZSA9ICJBcyBmaXNobmV0IGNlbnRyb2lkcyIpICsKICAgbWFwVGhlbWUKCmBgYAoKUmVmb3JtYXQgMjAxOSBMYW5kIENvdmVyIGFuZCBvdGhlciBmZWF0dXJlcyBmb3IgTW9kZWwsIHByZXNlcnZpbmcgMjAwMSBmZWF0dXJlcyBhcyBuZXcgY29sdW1ucy4KCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KZGF0MiA8LSBkYXQyICU+JQojIDIwMDEgbGFuZCBjb3ZlcgogICAgbXV0YXRlKGZhcm0wMSA9IGZhcm0pICU+JQogICAgbXV0YXRlKGZvcmVzdDAxID0gZm9yZXN0KSAlPiUKICAgIG11dGF0ZSh3ZXRsYW5kczAxID0gd2V0bGFuZHMpICU+JQogICAgbXV0YXRlKHdhdGVyMDEgPSB3YXRlcikgJT4lCiAgICBtdXRhdGUob3RoZXJVbmRldmVsb3BlZDAxID0gb3RoZXJVbmRldmVsb3BlZCkgJT4lCiAgICBtdXRhdGUodG90YWxfaG91c2luZ191bml0czAxID0gdG90YWxfaG91c2luZ191bml0cykgJT4lCiAgICBtdXRhdGUodG90YWxfd2hpdGUwMSA9IHRvdGFsX3doaXRlKSAlPiUgCiMgMjAxOSBsYW5kIGNvdmVyCiAgICBtdXRhdGUoZmFybSA9IGZhcm0xOSkgJT4lCiAgICBtdXRhdGUoZm9yZXN0ID0gZm9yZXN0MTkpICU+JQogICAgbXV0YXRlKHdldGxhbmRzID0gd2V0bGFuZHMxOSkgJT4lCiAgICBtdXRhdGUod2F0ZXIgPSB3YXRlcjE5KSAlPiUKICAgIG11dGF0ZShvdGhlclVuZGV2ZWxvcGVkID0gb3RoZXJVbmRldmVsb3BlZDE5KSAlPiUKICAgIG11dGF0ZSh0b3RhbF9ob3VzaW5nX3VuaXRzID0gdG90YWxfaG91c2luZ191bml0czIwMjApICU+JQogICAgbXV0YXRlKHRvdGFsX3doaXRlID0gdG90YWxfd2hpdGUyMDIwKQoKCmBgYAoKIyMgNi4zLiBTZW5zaXRpdmUgTGFuZCBDb3ZlciBMb3N0CgpCZWxvdyBhbiBpbmRpY2F0b3IgYHNlbnNpdGl2ZV9sb3N0YCBpcyBjcmVhdGVkIGluZGljYXRpbmcgZ3JpZCBjZWxscyB0aGF0IHdlcmUgZWl0aGVyIGZvcmVzdCBvciB3ZXRsYW5kcyBpbiAyMDAxIGJ1dCB3ZXJlIG5vIGxvbmdlciBzbyBpbiAyMDE5LiBUaGUgb3V0cHV0IGxheWVyLCBgc2Vuc2l0aXZlX2xhbmRfbG9zdGAsIGdpdmVzIGEgc2Vuc2UgZm9yIGhvdyBkZXZlbG9wbWVudCBpbiB0aGUgcmVjZW50IHBhc3QgaGFzIGVmZmVjdGVkIHRoZSBuYXR1cmFsIGVudmlyb25tZW50LgoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoPSA2fQpkYXQyIDwtCiAgZGF0MiAlPiUKICAgbXV0YXRlKHNlbnNpdGl2ZV9sb3N0MTkgPSBpZmVsc2UoZm9yZXN0MDEgPT0gMSAmIGZvcmVzdDE5ID09IDAgfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3ZXRsYW5kczAxID09IDEgJiB3ZXRsYW5kczE5ID09IDAsMSwwKSkKICAgICAgICAgICAgICAgICAgICAgIApnZ3Bsb3QoKSArCiAgZ2VvbV9zZihkYXRhPWRhdDIsIGFlcyhmaWxsID1hcy5mYWN0b3Ioc2Vuc2l0aXZlX2xvc3QxOSkpLCBjb2xvciA9ICJ0cmFuc3BhcmVudCIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwKICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJObyBDaGFuZ2UiLCJTZW5zaXRpdmUgTG9zdCIpLAogICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICIiKSArCiAgbGFicyh0aXRsZSA9ICJTZW5zaXRpdmUgbGFuZHMgbG9zdDogMjAwMSAtIDIwMTkiKSArCiAgbWFwVGhlbWUKYGBgCgojIyA2LjQgTGFuZHNjYXBlIEZyYWdtZW50YXRpb24KCkluIHRoaXMgc2VjdGlvbiwgdGhlIGB3ZXRsYW5kczExYCBhbmQgYGZvcmVzdDExYCByYXN0ZXJzIGFyZSBjb252ZXJ0ZWQgdG8gY29udGlndW91cyBgc2Vuc2l0aXZlX3JlZ2lvbnNgIHVzaW5nIHRoZSBgcmFzdGVyOjpjbHVtcGAgZnVuY3Rpb24uIFRoaXMgaXMgZXF1aXZhbGVudCB0byBSZWdpb24gR3JvdXAgaW4gQXJjR0lTLiBUaGUgcmFzdGVyIGNsdW1wcyBhcmUgdGhlbiBjb252ZXJ0ZWQgdG8gdmVjdG9yIGBzZmAgbGF5ZXJzOyBkaXNzb2x2ZWQgaW50byB1bmlxdWUgcmVnaW9uczsgQWNyZXMgYXJlIGNhbGN1bGF0ZWQ7IGFuZCB0aGUgbGF5ZXJzIGFyZSBjb252ZXJ0ZWQgYmFjayB0byByYXN0ZXIgdG8gYmUgZXh0cmFjdGVkIGJhY2sgdG8gdGhlIGZpc2huZXQgd2l0aCBgYWdncmVnYXRlUmFzdGVyYC4gTm90ZSB0aGF0IG9ubHkgYHNlbnNpdGl2ZV9yZWdpb25zYCB3aXRoIGFyZWFzIGdyZWF0ZXIgdGhhbiAxIGFjcmUgYXJlIGluY2x1ZGVkLgoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoPSA2fQpzZW5zaXRpdmVSZWdpb25zIDwtIAogIHJhc3Rlcjo6Y2x1bXAod2V0bGFuZHMxOSArIGZvcmVzdDE5KSAlPiUKICByYXN0ZXJUb1BvbHlnb25zKCkgJT4lCiAgc3RfYXNfc2YoKSAlPiUKICBncm91cF9ieShjbHVtcHMpICU+JSAKICBzdW1tYXJpemUoKSAlPiUKICAgIG11dGF0ZShBY3JlcyA9IGFzLm51bWVyaWMoc3RfYXJlYSguKSAqIDAuMDAwMDIyOTU2OCkpICU+JQogICAgZmlsdGVyKEFjcmVzID4gMzk1NCkgICU+JQogIGRwbHlyOjpzZWxlY3QoKSAlPiUKICByYXN0ZXI6OnJhc3Rlcml6ZSguLGVtcHR5UmFzdGVyKSAKc2Vuc2l0aXZlUmVnaW9uc1tzZW5zaXRpdmVSZWdpb25zID4gMF0gPC0gMSAgCm5hbWVzKHNlbnNpdGl2ZVJlZ2lvbnMpIDwtICJzZW5zaXRpdmVSZWdpb25zIgoKZGF0MiA8LQogIGFnZ3JlZ2F0ZVJhc3RlcihjKHNlbnNpdGl2ZVJlZ2lvbnMpLCBkYXQyKSAlPiUKICBkcGx5cjo6c2VsZWN0KHNlbnNpdGl2ZVJlZ2lvbnMpICU+JQogIHN0X3NldF9nZW9tZXRyeShOVUxMKSAlPiUKICBiaW5kX2NvbHMoLixkYXQyKSAlPiUKICBzdF9zZigpCgpnZ3Bsb3QoKSArCiAgICBnZW9tX3NmKGRhdGE9ZGF0MiwgYWVzKGZpbGwgPWFzLmZhY3RvcihzZW5zaXRpdmVSZWdpb25zKSksIGNvbG9yID0gInRyYW5zcGFyZW50IikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUyLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIk90aGVyIiwiU2Vuc2l0aXZlIFJlZ2lvbnMiKSwKICAgICAgICAgICAgICAgICAgICAgIG5hbWU9IiIpICsKICBsYWJzKHRpdGxlID0gIlNlbnNpdGl2ZSByZWdpb25zIiwKICAgICAgIHN1YnRpdGxlID0gIkNvbnRpbm91cyBhcmVhcyBvZiBlaXRoZXIgd2V0bGFuZHMgb3IgZm9yZXN0c1xuZ3JlYXRlciB0aGFuIDEgYWNyZSIpICsKICBtYXBUaGVtZQpgYGAKCiMjIDYuNS4gU3VtbWFyaXplIGJ5IENvdW50eQoKVGhlIGJlbG93IGBkcGx5cmAgc3RhdGVtZW50IHRha2VzIGFzIGl0cyBpbnB1dCwgYGRhdDJgLCB3aGljaCB3YXMgY3JlYXRlZCBpbiBTZWN0aW9ucyA2LjIgLSA2LjQgYW5kIHdyYW5nbGVzIHRvZ2V0aGVyIGEgdGFibGUgb2YgY291bnR5LWxldmVsLCBzdXBwbHkgYW5kIGRlbWFuZCBtZXRyaWNzIHdoaWNoIGNhbiBiZSB1c2VkIHRvIGFuYWx5emUgc3VpdGFiaWxpdHkgYnkgY291bnR5LgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmNvdW50eV9zcGVjaWZpY19tZXRyaWNzIDwtIAogIGRhdDIgJT4lCiAgI3ByZWRpY3QgZGV2ZWxvcG1lbnQgZGVtYW5kIGZyb20gb3VyIG1vZGVsCiAgbXV0YXRlKERldmVsb3BtZW50X0RlbWFuZCA9IHByZWRpY3QoTW9kZWw2LCBkYXQyLCB0eXBlPSJyZXNwb25zZSIpKSAlPiUKICAjZ2V0IGEgY291bnQgY291bnQgb2YgZ3JpZCBjZWxscyBieSBjb3VudHkgd2hpY2ggd2UgY2FuIHVzZSB0byBjYWxjdWxhdGUgcmF0ZXMgYmVsb3cKICBsZWZ0X2pvaW4oc3Rfc2V0X2dlb21ldHJ5KGRhdCwgTlVMTCkgJT4lIGdyb3VwX2J5KE5BTUUpICU+JSBzdW1tYXJpemUoY291bnQgPSBuKCkpKSAlPiUKICAjY2FsY3VsYXRlIHN1bW1hcnkgc3RhdGlzdGljcyBieSBjb3VudHkKICBncm91cF9ieShOQU1FKSAlPiUKICBzdW1tYXJpemUoVG90YWxfRmFybWxhbmQgPSBzdW0oZmFybTE5KSAvIG1heChjb3VudCksCiAgICAgICAgICAgIFRvdGFsX0ZvcmVzdCA9IHN1bShmb3Jlc3QxOSkgLyBtYXgoY291bnQpLAogICAgICAgICAgICBUb3RhbF9XZXRsYW5kcyA9IHN1bSh3ZXRsYW5kczE5KSAvIG1heChjb3VudCksCiAgICAgICAgICAgIFRvdGFsX1VuZGV2ZWxvcGVkID0gc3VtKG90aGVyVW5kZXZlbG9wZWQxOSkgLyBtYXgoY291bnQpLAogICAgICAgICAgICBTZW5zaXRpdmVfTGFuZF9Mb3N0ID0gc3VtKHNlbnNpdGl2ZV9sb3N0MTkpIC8gbWF4KGNvdW50KSwKICAgICAgICAgICAgU2Vuc2l0aXZlX1JlZ2lvbnMgPSBzdW0oc2Vuc2l0aXZlUmVnaW9ucykgLyBtYXgoY291bnQpLAogICAgICAgICAgICBNZWFuX0RldmVsb3BtZW50X0RlbWFuZCA9IG1lYW4oRGV2ZWxvcG1lbnRfRGVtYW5kKSkgJT4lCiAgI2dldCBwb3B1bGF0aW9uIGRhdGEgYnkgY291bnR5CiAgbGVmdF9qb2luKGNvdW50eVBvcHVsYXRpb25fMjA0MCAlPiUgCiAgICAgICAgICAgIG11dGF0ZShQb3B1bGF0aW9uX0NoYW5nZSA9IGNvdW50eV9wcm9qZWN0aW9uXzIwNDAgLSBjb3VudHlfcG9wdWxhdGlvbl8yMDIwLAogICAgICAgICAgICAgICAgICAgUG9wdWxhdGlvbl9DaGFuZ2VfUmF0ZSA9IFBvcHVsYXRpb25fQ2hhbmdlIC8gY291bnR5X3Byb2plY3Rpb25fMjA0MCkgJT4lCiAgICAgICAgICAgIGRwbHlyOjpzZWxlY3QoTkFNRSxQb3B1bGF0aW9uX0NoYW5nZV9SYXRlKSkKYGBgCgoKTm93IGEgc21hbGwgbXVsdGlwbGUgcGxvdCBjYW4gYmUgY3JlYXRlZCBwcm92aWRpbmcgYm90aCBzdXBwbHkgYW5kIGRlbWFuZCBzaWRlIGFuYWx5dGljcyBieSBjb3VudHkuIFRoZSBwbG90IGdpdmVzIGEgc2Vuc2UgZm9yIGRldmVsb3BtZW50IGRlbWFuZCAoYERlbWFuZC1TaWRlYCksIHN1aXRhYmxlIGxhbmQgZm9yIGRldmVsb3BtZW50IChgU3VpdGFibGVgKSBhbmQgc2Vuc2l0aXZlIGxhbmQgKGBOb3QgU3VpdGFibGVgKS4KClRoZSBkYXRhIHN1Z2dlc3RzIGJvdGggcG9wdWxhdGlvbiBhbmQgZGV2ZWxvcG1lbnQgZGVtYW5kIHdpbGwgaW5jcmVhc2UgZm9yIENoYXJsb3R0ZXN2aWxsZSBNU0EuIEhvd2V2ZXIsIGNvbXBhcmVkIHRvIENoYXJsb3R0ZXN2aWxsZSBjb3VudHksIEFsYmVtYXJsZSBoYXMgYSBoaWdoIHJhdGUgb2YgZGV2ZWxvcGFibGUgZmFybWxhbmQgYW5kIGEgbG93IHN1cHBseSBvZiBzZW5zaXRpdmUgbGFuZC4gQWxiZW1hcmxlb3J0IGlzIHdlbGwgc3VpdGFibGUgdG8gbmV3IGRldmVsb3BtZW50IHRoYW4gQ2hhcmxvdHRlc3ZpbGxlLCB3aGVyZSB0aGUgbGF0dGVyIGhhcyBsb3dlciBzdXBwbHkgb2YgZGV2ZWxvcGFibGUgbGFuZHMuCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmNvdW50eV9zcGVjaWZpY19tZXRyaWNzICU+JQogIGdhdGhlcihWYXJpYWJsZSwgVmFsdWUsIC1OQU1FLCAtZ2VvbWV0cnkpICU+JQogIG11dGF0ZShWYXJpYWJsZSA9IGZhY3RvcihWYXJpYWJsZSwgbGV2ZWxzPWMoIlBvcHVsYXRpb25fQ2hhbmdlX1JhdGUiLCJNZWFuX0RldmVsb3BtZW50X0RlbWFuZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVG90YWxfRmFybWxhbmQiLCJUb3RhbF9VbmRldmVsb3BlZCIsIlRvdGFsX0ZvcmVzdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVG90YWxfV2V0bGFuZHMiLCJTZW5zaXRpdmVfTGFuZF9Mb3N0IiwiU2Vuc2l0aXZlX1JlZ2lvbnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXJlZCA9IFRSVUUpKSkgJT4lCiAgbXV0YXRlKFBsYW5uaW5nX0Rlc2lnbmF0aW9uID0gY2FzZV93aGVuKAogICAgVmFyaWFibGUgPT0gIlBvcHVsYXRpb25fQ2hhbmdlX1JhdGUiIHwgVmFyaWFibGUgPT0gIk1lYW5fRGV2ZWxvcG1lbnRfRGVtYW5kIiB+ICJEZW1hbmQtU2lkZSIsCiAgICBWYXJpYWJsZSA9PSAiVG90YWxfRmFybWxhbmQiIHwgVmFyaWFibGUgPT0gIlRvdGFsX1VuZGV2ZWxvcGVkIiAgICAgICAgICAgICAgIH4gIlN1aXRhYmxlIiwKICAgIFRSVUUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfiAiTm90IFN1aXRhYmxlIikpICU+JQogIGdncGxvdChhZXMoeD1WYXJpYWJsZSwgeT1WYWx1ZSwgZmlsbD1QbGFubmluZ19EZXNpZ25hdGlvbikpICsKICAgIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249cG9zaXRpb25fZG9kZ2UoKSwgY29sb3VyPSJibGFjayIpICsKICAgIGZhY2V0X3dyYXAofk5BTUUsIG5jb2w9NSkgKwogICAgY29vcmRfZmxpcCgpICsKICAgIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoLjI1LCAxLCBieSA9IC4yNSkpICsKICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDIuNSkgKyBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSA0LjUpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCJibGFjayIsInJlZCIsImRhcmtncmVlbiIpKSArCiAgICBsYWJzKHRpdGxlPSAiQ291bnR5IFNwZWNpZmljIEFsbG9jYXRpb24gTWV0cmljcyIsIHN1YnRpdGxlPSAiQXMgcmF0ZXMiLCB4PSJJbmRpY2F0b3IiLCB5PSJSYXRlIikgKwogICAgcGxvdFRoZW1lICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwgbGVnZW5kLnBvc2l0aW9uPSJib3R0b20iKQpgYGAKCiMgNy4gQWxsb2NhdGlvbgoKQWxsb2NhdGlvbiBpcyB0aGUgZmluYWwgc3RhZ2Ugb2YgdGhlIHVyYmFuIGdyb3d0aCBtb2RlbGluZyBwcm9jZXNzLiBOb3cgdGhhdCBib3RoIGRlbWFuZCBhbmQgc3VwcGx5IGlzIHVuZGVyc3Rvb2QsIFdlIGNhbiBhbGxvY2F0ZSBkZXZlbG9wbWVudCByaWdodHMgYWNjb3JkaW5nbHkuIE9mIGNvdXJzZSwgdGhpcyBjb3VsZCB0YWtlIG1hbnkgZm9ybXMgb2YgcmVndWxhdGlvbiBpbmNsdWRpbmcgem9uaW5nLCBzdWJkaXZpc2lvbiBhcHByb3ZhbCBvciBvdXRyaWdodCBjb25zZXJ2YXRpb24uIEluIHRoaXMgc2VjdGlvbiwgZGVtYW5kIGFuZCBzdXBwbHkgYXJlIHZpc3VhbGl6ZWQgZm9yIHR3byBjb3VudGllcywgQ2hhcmxvdHRlc3ZpbGxlIGFuZCBBbGJlbWFybGUgVGhlIGRhdGEgc3VnZ2VzdHMgdGhhdCB0aGUgbGF0dGVyIGlzIG1vcmUgY29uZHVjaXZlIHRvIGdyb3d0aCB3aGlsZSB0aGUgZm9ybWVyLCBsZXNzIHNvLgoKRmlyc3QsIGRldmVsb3BtZW50IGRlbWFuZCBpcyBwcmVkaWN0ZWQgZm9yIEFsYmVtYXJsZS4gVGhlbiBhIGxheWVyLCBgQWxiZW1hcmxlX2xhbmRVc2VgIGlzIGNyZWF0ZWQsIHRoYXQgaW5jbHVkZXMgaW5kaWNhdG9ycyBmb3IgYm90aCBwcmV2aW91c2x5IGRldmVsb3BlZCBsYW5kIGFuZCBlbnZpcm9ubWVudGFsbHkgdW5zdWl0YWJsZSBsYW5kLiBUaGlzIGxheWVyIHRoZW4gaXMgb3ZlcmxheWVkIGF0b3AgZGV2ZWxvcG1lbnQgZGVtYW5kIGFuZCBwcm9qZWN0ZWQgcG9wdWxhdGlvbiBjaGFuZ2UgdG8gZ2l2ZSB0aGUgZnVsbCBzdXBwbHkgYW5kIGRlbWFuZC1zaWRlIHBpY3R1cmUgaW4gQWxiZW1hcmxlLgoKVGhlcmUgYXJlIHNvbWUgY2xlYXIgb3Bwb3J0dW5pdGllcyBmb3IgZGV2ZWxvcG1lbnQgaW4gQWxiZW1hcmxlLiBTaWduaWZpY2FudCBpbmZpbGwgb3Bwb3J0dW5pdGllcyBleGlzdCBhbG9uZyB0aGUgcm9hZHMgYW5kIGhpZ2h3YXlzIHdoZXJlIHBvcHVsYXRpb24gY2hhbmdlIGlzIHByb2plY3RlZCB0byBiZSBncmVhdGVzdC4gVGhlcmUgaXMgYWxzbyBhIGdvb2QgZGVhbCBvZiBlbnZpcm9ubWVudGFsbHkgc3VpdGFibGUgbGFuZCBhbG9uZyB0aGUgaGlnaHdheXMuIFRoaXMgd291bGQgYmUgaWRlYWwgc3BhY2UgZm9yIGxhbmQgZGV2ZWxvcG1lbnRzLgoKPGRpdiBjbGFzcz0ic3VwZXJiaWdpbWFnZSI+CmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmhlaWdodD0gOCwgZmlnLndpZHRoPSAxMX0KQWxiZW1hcmxlIDwtCiAgZGF0MiAlPiUgICAjY2FsY3VsYXRlIHBvcHVsYXRpb24gY2hhbmdlCiAgICBsZWZ0X2pvaW4oY291bnR5UG9wdWxhdGlvbl8yMDQwKSAlPiUKICAgIG11dGF0ZShwcm9wb3J0aW9uX29mX2NvdW50eV9wb3AgPSB0b3RhbF9wb3B1bGF0aW9uMjAyMCAvIGNvdW50eV9wb3B1bGF0aW9uXzIwMjAsCiAgICAgICAgICAgcG9wXzIwNDAuaW5maWxsID0gcHJvcG9ydGlvbl9vZl9jb3VudHlfcG9wICogY291bnR5X3Byb2plY3Rpb25fMjA0MCwKICAgICAgICAgICBwb3BfQ2hhbmdlID0gKHBvcF8yMDQwLmluZmlsbCAtIHRvdGFsX3BvcHVsYXRpb24yMDIwKSkgJT4lCiAgICBkcGx5cjo6c2VsZWN0KC1jb3VudHlfcHJvamVjdGlvbl8yMDQwLCAtY291bnR5X3BvcHVsYXRpb25fMjAyMCwgCiAgICAgICAgICAgICAgICAgIC1wcm9wb3J0aW9uX29mX2NvdW50eV9wb3AsIC1wb3BfMjA0MC5pbmZpbGwpICU+JQogICAgbXV0YXRlKERldmVsb3BtZW50X0RlbWFuZCA9IHByZWRpY3QoTW9kZWw2LCBkYXQyLCB0eXBlPSJyZXNwb25zZSIpKSAlPiUKICAgIGZpbHRlcihOQU1FID09ICJBbGJlbWFybGUiKSAKCgpBbGJlbWFybGVfbGFuZFVzZSA8LSByYmluZCgKICBmaWx0ZXIoQWxiZW1hcmxlLCBmb3Jlc3QxOSA9PSAxIHwgd2V0bGFuZHMxOSA9PSAxICkgJT4lCiAgZHBseXI6OnNlbGVjdCgpICU+JSBtdXRhdGUoTGFuZF9Vc2UgPSAiTm90IFN1aXRhYmxlIiksCiAgZmlsdGVyKEFsYmVtYXJsZSwgZGV2ZWxvcGVkMTkgPT0gMSkgJT4lCiAgZHBseXI6OnNlbGVjdCgpICU+JSBtdXRhdGUoTGFuZF9Vc2UgPSAiRGV2ZWxvcGVkIikpCgpncmlkLmFycmFuZ2UoCmdncGxvdCgpICsKICBnZW9tX3NmKGRhdGE9QWxiZW1hcmxlLCBhZXMoZmlsbD1mYWN0b3IobnRpbGUoRGV2ZWxvcG1lbnRfRGVtYW5kLDUpKSksIGNvbG91cj1OQSkgKwogIGdlb21fcG9pbnQoZGF0YT1BbGJlbWFybGVfbGFuZFVzZSwgYWVzKHg9eHlDKEFsYmVtYXJsZV9sYW5kVXNlKVssMV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5PXh5QyhBbGJlbWFybGVfbGFuZFVzZSlbLDJdLCBjb2xvdXI9TGFuZF9Vc2UpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hhcGUgPSAxNiwgc2l6ZSA9IDAuMykgKwogIGdlb21fc2YoZGF0YT1zdF9pbnRlcnNlY3Rpb24oQ3ZpbGxlSGlnaHdheXMsZmlsdGVyKHN0dWR5QXJlYUNvdW50aWVzLCBOQU1FPT0iQWxiZW1hcmxlIikpLCBzaXplPTIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNSwgbmFtZT0iRGV2ZWxvcG1lbnRfRGVtYW5kIiwKICAgICAgICAgICAgICAgICAgICBsYWJlbHM9c3Vic3RyKHF1aW50aWxlQnJlYWtzKEFsYmVtYXJsZSwiRGV2ZWxvcG1lbnRfRGVtYW5kIiksMSw1KSkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiYmxhY2siLCJyZWQiKSkgKwogIGxhYnModGl0bGUgPSAiRGV2ZWxvcG1lbnQgUG90ZW50aWFsLCBcbjIwNDA6IEFsYmVtYXJsZSIpICsgbWFwVGhlbWUgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMSksIGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDIpKSwKCmdncGxvdCgpICsKICBnZW9tX3NmKGRhdGE9QWxiZW1hcmxlLCBhZXMoZmlsbD1mYWN0b3IobnRpbGUocG9wX0NoYW5nZSw1KSkpLCBjb2xvdXI9TkEpICsKICBnZW9tX3BvaW50KGRhdGE9QWxiZW1hcmxlX2xhbmRVc2UsIGFlcyh4PXh5QyhBbGJlbWFybGVfbGFuZFVzZSlbLDFdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeT14eUMoQWxiZW1hcmxlX2xhbmRVc2UpWywyXSwgY29sb3VyPUxhbmRfVXNlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNoYXBlID0gMTYsIHNpemUgPSAwLjMpICsKICBnZW9tX3NmKGRhdGE9c3RfaW50ZXJzZWN0aW9uKEN2aWxsZUhpZ2h3YXlzLGZpbHRlcihzdHVkeUFyZWFDb3VudGllcywgTkFNRT09IkFsYmVtYXJsZSIpKSwgc2l6ZT0yKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsIG5hbWU9IlBvcHVsYXRpb25fQ2hhbmdlIiwKICAgICAgICAgICAgICAgICAgICBsYWJlbHM9c3Vic3RyKHF1aW50aWxlQnJlYWtzKEFsYmVtYXJsZSwicG9wX0NoYW5nZSIpLDEsNSkpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoImJsYWNrIiwicmVkIikpICsKICBsYWJzKHRpdGxlID0gIlByb2plY3RlZCBQb3B1bGF0aW9uLCBcbjIwNDA6IEFsYmVtYXJsZSIpICsgbWFwVGhlbWUgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMSksIGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDIpKSwgbmNvbD0yKQoKYGBgCgpUaGUgcGxvdHMgYWJvdmUgYXJlIGNyZWF0ZWQgdXNpbmcgYSBgZ2dwbG90YCB0cmljayB0byBzaG93IHdoYXQgYXBwZWFycyB0byBiZSBvdmVybGF5ZWQgcG9seWdvbnMgKGZpc2huZXQgZ3JpZCBjZWxscykuIAoKRm9yIGNvbXBhcmlzb24gcHVycG9zZXMsIHRoaXMgcHJvY2VzcyBpcyByZXBsaWNhdGVkIGZvciBDaGFybG90dGVzdmlsbGUgY291bnR5IGJlbG93LiBUaGVyZSBpcyB3YXkgbGVzcyBhdmFpbGFibGUgbGFuZHMgZm9yIGdyb3d0aC4KCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmhlaWdodD0gOCwgZmlnLndpZHRoPSAxMX0KQ2hhcmxvdHRlc3ZpbGxlIDwtCiAgZGF0MiAlPiUgICAjY2FsY3VsYXRlIHBvcHVsYXRpb24gY2hhbmdlCiAgICBsZWZ0X2pvaW4oY291bnR5UG9wdWxhdGlvbl8yMDQwKSAlPiUKICAgIG11dGF0ZShwcm9wb3J0aW9uX29mX2NvdW50eV9wb3AgPSB0b3RhbF9wb3B1bGF0aW9uMjAyMCAvIGNvdW50eV9wb3B1bGF0aW9uXzIwMjAsCiAgICAgICAgICAgcG9wXzIwNDAuaW5maWxsID0gcHJvcG9ydGlvbl9vZl9jb3VudHlfcG9wICogY291bnR5X3Byb2plY3Rpb25fMjA0MCwKICAgICAgICAgICBwb3BfQ2hhbmdlID0gKHBvcF8yMDQwLmluZmlsbCAtIHRvdGFsX3BvcHVsYXRpb24yMDIwKSkgJT4lCiAgICBkcGx5cjo6c2VsZWN0KC1jb3VudHlfcHJvamVjdGlvbl8yMDQwLCAtY291bnR5X3BvcHVsYXRpb25fMjAyMCwgCiAgICAgICAgICAgICAgICAgIC1wcm9wb3J0aW9uX29mX2NvdW50eV9wb3AsIC1wb3BfMjA0MC5pbmZpbGwpICU+JQogICAgbXV0YXRlKERldmVsb3BtZW50X0RlbWFuZCA9IHByZWRpY3QoTW9kZWw2LCBkYXQyLCB0eXBlPSJyZXNwb25zZSIpKSAlPiUKICAgIGZpbHRlcihOQU1FID09ICJDaGFybG90dGVzdmlsbGUiKSAKCgpDaGFybG90dGVzdmlsbGVfbGFuZFVzZSA8LSByYmluZCgKICBmaWx0ZXIoQ2hhcmxvdHRlc3ZpbGxlLCBmb3Jlc3QxOSA9PSAxIHwgd2V0bGFuZHMxOSA9PSAxICkgJT4lCiAgZHBseXI6OnNlbGVjdCgpICU+JSBtdXRhdGUoTGFuZF9Vc2UgPSAiTm90IFN1aXRhYmxlIiksCiAgZmlsdGVyKENoYXJsb3R0ZXN2aWxsZSwgZGV2ZWxvcGVkMTkgPT0gMSkgJT4lCiAgZHBseXI6OnNlbGVjdCgpICU+JSBtdXRhdGUoTGFuZF9Vc2UgPSAiRGV2ZWxvcGVkIikpCgpncmlkLmFycmFuZ2UoCmdncGxvdCgpICsKICBnZW9tX3NmKGRhdGE9Q2hhcmxvdHRlc3ZpbGxlLCBhZXMoZmlsbD1mYWN0b3IobnRpbGUoRGV2ZWxvcG1lbnRfRGVtYW5kLDUpKSksIGNvbG91cj1OQSkgKwogIGdlb21fcG9pbnQoZGF0YT1DaGFybG90dGVzdmlsbGVfbGFuZFVzZSwgYWVzKHg9eHlDKENoYXJsb3R0ZXN2aWxsZV9sYW5kVXNlKVssMV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5PXh5QyhDaGFybG90dGVzdmlsbGVfbGFuZFVzZSlbLDJdLCBjb2xvdXI9TGFuZF9Vc2UpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hhcGUgPSAxNiwgc2l6ZSA9IDEpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNSwgbmFtZT0iRGV2ZWxvcG1lbnRfRGVtYW5kIiwKICAgICAgICAgICAgICAgICAgICBsYWJlbHM9c3Vic3RyKHF1aW50aWxlQnJlYWtzKENoYXJsb3R0ZXN2aWxsZSwiRGV2ZWxvcG1lbnRfRGVtYW5kIiksMSw1KSkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiYmxhY2siLCJyZWQiKSkgKwogIGxhYnModGl0bGUgPSAiRGV2ZWxvcG1lbnQgUG90ZW50aWFsLCBcbjIwNDA6IENoYXJsb3R0ZXN2aWxsZSIpICsgbWFwVGhlbWUgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMSksIGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDIpKSwKCmdncGxvdCgpICsKICBnZW9tX3NmKGRhdGE9Q2hhcmxvdHRlc3ZpbGxlLCBhZXMoZmlsbD1mYWN0b3IobnRpbGUocG9wX0NoYW5nZSw1KSkpLCBjb2xvdXI9TkEpICsKICBnZW9tX3BvaW50KGRhdGE9Q2hhcmxvdHRlc3ZpbGxlX2xhbmRVc2UsIGFlcyh4PXh5QyhDaGFybG90dGVzdmlsbGVfbGFuZFVzZSlbLDFdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeT14eUMoQ2hhcmxvdHRlc3ZpbGxlX2xhbmRVc2UpWywyXSwgY29sb3VyPUxhbmRfVXNlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNoYXBlID0gMTYsIHNpemUgPSAxKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsIG5hbWU9IlBvcHVsYXRpb25fQ2hhbmdlIiwKICAgICAgICAgICAgICAgICAgICBsYWJlbHM9c3Vic3RyKHF1aW50aWxlQnJlYWtzKEFsYmVtYXJsZSwicG9wX0NoYW5nZSIpLDEsNSkpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoImJsYWNrIiwicmVkIikpICsKICBsYWJzKHRpdGxlID0gIlByb2plY3RlZCBQb3B1bGF0aW9uLCBcbjIwNDA6IENoYXJsb3R0ZXN2aWxsZSIpICsgbWFwVGhlbWUgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMSksIGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDIpKSwgbmNvbD0yKQoKYGBgCgoKV2Ugc3RvcCBzaG9ydCBpbiBhY3R1YWxseSBhbGxvY2F0aW5nIGxhbmQgdG8gZGV2ZWxvcG1lbnQuIFdoaWxlIHRoZSBtb2RlbCBpcyB3ZWxsIHN1aXRlZCBmb3IgdW5kZXJzdGFuZGluZyBzcHJhd2wtc3R5bGUgZGV2ZWxvcG1lbnQsIGl0IGlzIG5vdCB1c2VmdWwgZm9yIHVuZGVyc3RhbmRpbmcgaG93IG5ldyBkZW1hbmQgbWlnaHQgYmUgYWJzb3JiZWQgYnkgdXB6b25pbmcgYW5kIGRlbnNpZmljYXRpb24gb2YgZXhpc3RpbmcgZGV2ZWxvcG1lbnQuIEl0IHdvdWxkIG5vdCBiZSB3aXNlIHRvIGFsbG9jYXRlIHRoZSBlbnRpcmUgcHJvamVjdGVkIHBvcHVsYXRpb24gdG8gdW5kZXZlbG9wZWQgbGFuZC4gSW5zdGVhZCwgd2XigJlkIHByZWZlciBhIG1vcmUgbnVhbmNlZCB1bmRlcnN0YW5kaW5nIG9mIGhvdyBsb2NhbCBsYW5kIHVzZSBsYXdzIG1pZ2h0IHBsYXkgYSByb2xlLiBBdCB0aGlzIHN0YWdlIGluIHRoZSBhbmFseXNpcyBob3dldmVyLCB0aGUgUGxhbm5lciBoYXMgYWxsIHNoZSBuZWVkcyB0byBlbmdhZ2UgbG9jYWwgc3Rha2Vob2xkZXJzIGFib3V0IGZ1dHVyZSBkZXZlbG9wbWVudCBkZWNpc2lvbnMuCg==